我写了以下内容:
(fn r [f xs]
(lazy-seq
(if (empty? xs)
'()
(cons (f (first xs)) (r f (rest xs))))))
为了解决4clojure.com的问题#118:http://www.4clojure.com/problem/118,需要重新实现不使用map等的map函数,并且该解决方案通过了测试(我不知道它是否正确:它非常接近其他解决方案)。因为问题要求必须是lazy,所以我通过将我的解决方案包装在lazy-seq中来编写上面的代码......但是我不明白这里的“lazy”是什么,也不知道如何测试它。当我询问(type ...)时,我得到的是一个clojure.lang.LazySeq,但我不知道它和如果我简单地删除lazy-seq“包装”得到的结果有什么区别。当然,如果我删除lazy-seq,尝试执行以下操作会导致stackoverflow:
(= [(int 1e6) (int (inc 1e6))]
(->> (... inc (range))
(drop (dec 1e6))
(take 2)))
否则(也就是说:如果我让惰性序列包装保持不变),它似乎可以正常工作。
因此,我决定尝试以某种方式“调试”/跟踪正在发生的事情,以尝试理解它是如何工作的。我使用了以下宏(我记得是在stackoverflow上找到的):
(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#))
我将工作版本包裹在dbg宏中并尝试再次执行。现在糟糕了:之前正常工作的版本现在也会抛出堆栈溢出。
我不确定:也许这是宏的一个不良影响,它会以某种方式强制评估本来不会被评估的东西?
如果有人能够使用这个简单的函数和简单的测试来解释懒惰是如何工作的,什么时候确切地调用了什么等等,那就太好了。