我写了以下内容:
(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-seq 中“包装”我的解决方案来编写上面的代码...但是我不明白 lazy是怎么回事-seq 有效。
我不明白这里什么是“懒惰”,也不知道如何测试它。
当我问(type ...)
时,毫不奇怪,我得到了一个 clojure.lang.LazySeq ,但我不知道如果我只是删除<它与我之间的区别是什么em> lazy-seq “wrap”。
当然,如果我删除 lazy-seq ,我会得到一个stackoverflow为什么要尝试执行此操作:
(= [(int 1e6) (int (inc 1e6))]
(->> (... inc (range))
(drop (dec 1e6))
(take 2)))
否则(即:如果我让lazy-seq包装到位),它似乎工作正常。
所以我决定尝试以某种方式“调试”/追踪正在发生的事情以试图理解它是如何工作的。我采用了以下宏(我在SO IIRC上找到):
(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#))
将工作版本包装在 dbg 宏中并尝试再次执行。而现在kaboom:现在运行良好的版本也会引发堆栈溢出。
现在我不确定:也许这是宏的一个不受欢迎的影响,会以某种方式强制评估那些否则不会被评估的东西?
如果有人能够解释,使用这个简单的函数和简单的测试,懒惰在这里是如何工作的,在什么时候调用什么等等,这将是很好的。
答案 0 :(得分:4)
整个魔法在于clojure.lang.LazySeq java类。它本身实现了ISeq接口,并且lazy-seq
宏的s-expressions参数被转换为没有任何参数的函数,并被传递给clojure.lang.LazySeq的构造函数(到构造函数中{{1} } object as parameter)因为最后你又调用了IFn
函数(返回r
而不是完整列表),这允许LazySeq懒惰地评估项目。
所以基本上流程是这样的:
ISeq
。返回的ISeq存储在类中的局部变量中。我知道这是一个有点心灵弯曲的东西:)。我刚才也经历了Java代码,并且在我意识到魔术是可能的之后能够弄清楚因为对r
本身的递归调用返回了一个懒惰的序列。所以你有它,一种自定义分隔的延续:)