Clojure 懒洋洋
我写了以下内容:Clojure 懒洋洋,clojure,stack-overflow,lazy-evaluation,lazy-sequences,Clojure,Stack Overflow,Lazy Evaluation,Lazy Sequences,我写了以下内容: (fn r [f xs] (lazy-seq (if (empty? xs) '() (cons (f (first xs)) (r f (rest xs)))))) 要解决4clojure.com的问题#118: 它要求在不使用map等的情况下重新实现map,并且该解决方案通过了测试(我不知道它是否正确:它与前面提到的其他解决方案非常接近) 因为这个问题说明它必须是懒惰的,所以我通过将我的解决方案“包装”在一个懒惰的序列中来编写上面的代码。。。然
(fn r [f xs]
(lazy-seq
(if (empty? xs)
'()
(cons (f (first xs)) (r f (rest xs))))))
要解决4clojure.com的问题#118:
它要求在不使用map等的情况下重新实现map,并且该解决方案通过了测试(我不知道它是否正确:它与前面提到的其他解决方案非常接近)
因为这个问题说明它必须是懒惰的,所以我通过将我的解决方案“包装”在一个懒惰的序列中来编写上面的代码。。。然而,我不明白lazy seq是如何工作的
我不明白这里的“懒惰”是什么,也不知道如何测试它
当我问(type…
时,毫不奇怪,我得到了一个clojure.lang.LazySeq,但我不知道这和如果我简单地删除懒惰的seq“wrapping”会得到什么区别
当然,如果我删除了懒惰的seq,我会得到一个stackoverflow,为什么要执行这个:
(= [(int 1e6) (int (inc 1e6))]
(->> (... inc (range))
(drop (dec 1e6))
(take 2)))
否则(也就是说:如果我让懒惰的seq包装就位),它似乎可以正常工作
因此,我决定尝试以某种方式“调试”/跟踪正在发生的事情,以试图了解它是如何工作的。我使用了以下宏(我在SO IIRC上找到):
并将工作版本包装到dbg宏中,然后再次尝试执行它。现在kaboom:运行良好的版本现在也抛出堆栈溢出
现在我不确定:也许这是宏的一个不必要的影响,它会以某种方式强制评估那些本来不会被评估的东西
如果有人能用这个简单的函数和简单的测试来解释lazyness在这里是如何工作的,什么时候被调用,等等,那就太好了。整个魔法在于java类。它本身实现ISeq接口,并且将s-expressions参数转换为不带任何参数的函数,并传递给clojure.lang.LazySeq的构造函数(传递给以IFn
对象为参数的构造函数)因为最后您再次调用了r
函数(返回的是ISeq
,而不是完整的列表),这就允许懒散的seq评估项目
基本上,流程是这样的:
- LazySeq调用传递给它的Fn(即代码的其余部分)
- 此Fn调用返回ISeq,因为列表实现ISeq。由于递归调用
,此返回ISeq(列表),第一个值作为具体值,第二个是LazySeq对象。返回的ISeq存储在类中的局部变量中r
- LazySeq在调用next item时的ISeq实现确实调用了它在上述步骤中存储在本地类变量中的下一个ISeq(列表),并检查它是否属于LazySeq类型(由于
call,它将位于第二个item中),如果是LazySeq,则计算并返回该值,然后返回item,否则直接返回该item(您传递给cons的第一个具体值)r
我知道这有点让人心烦意乱:)。我刚才还浏览了Java代码,在我意识到这一神奇是可能的之后,我发现了这一点,因为对
r
本身的递归调用返回了一个惰性序列。这里有一种自定义分隔的连续体:)这可能有助于澄清这个习惯用法中不涉及递归,因为r
的返回值是一个LazySeq
,只有在再次实现head调用r
时才会出现。实现总是在r
已返回后进行。Clojure具有“已实现”测试函数来测试是否已实现惰性序列的值。e、 g.(已实现?(范围))将为false。
(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#))