Recursion 递归使用'lazy seq'会占用堆栈帧吗?
目前我正在学习lazy-seq,我注意到它们通常涉及递归,而不使用Recursion 递归使用'lazy seq'会占用堆栈帧吗?,recursion,clojure,lazy-sequences,Recursion,Clojure,Lazy Sequences,目前我正在学习lazy-seq,我注意到它们通常涉及递归,而不使用recur。例如,下面是iterate的实现: (defn iterate "Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects" {:added "1.0" :static true} [f x] (cons x (lazy-seq (iterate f (f x))))) 我的印象是,如果您在不
recur
。例如,下面是iterate
的实现:
(defn iterate
"Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects"
{:added "1.0"
:static true}
[f x] (cons x (lazy-seq (iterate f (f x)))))
我的印象是,如果您在不使用recur
的情况下进行递归,它将为每个调用生成一个新的堆栈帧,如果您迭代足够多的时间,这可能会导致堆栈溢出
lazy seq
是否会吞噬堆栈帧?如果没有,它如何避免呢?没有懒惰的seq不会吞噬堆栈帧
从这段代码的表面上看,它似乎是一个递归调用,但lazy seq不是一个函数,它是一个宏,在这种特殊情况下,它将转换为:
(惰性seq(迭代f(f x))
变得有点像
(新的clojure.lang.LazySeq(fn[](迭代f(f x)))
正如您所看到的,迭代函数调用被包装在另一个函数中(这使它变得懒惰,即稍后将调用该函数).
在本例中,当您看到lazy seq包含一个迭代调用时,它实际上不是递归的。相反,当您请求第二个项时,lazy seq宏将自身展开为一个挂起的调用。因此,每次检索一个项时,您都会引起一次调用,但旧调用不在堆栈上
另一个有趣的问题是您是否正在消耗内存,这取决于您对序列头所做的操作。如果您保留在var或本地绑定中,整个cons单元格链将被保留。如果您避免保留头部,并沿序列向下移动,GC可以在您后面清理
一个棘手的问题是lazy-seq宏中有一些特殊的元^{:once-true},它让编译器知道fn闭包是一次性的,并且不保留任何闭合变量