具有lazy seq的Clojure尾递归
请您更宽泛、更清楚地解释一下,具有lazy seq的Clojure尾递归,clojure,Clojure,请您更宽泛、更清楚地解释一下,lazy seq如何按照本文档页面使尾部递归“安全” 如果您查看lazy seq的实现,您将看到它确实返回了一个闭包(一个保持上下文的函数): 因此,在(cons n(正数(inc n))中对正数的调用不会立即计算,而是延迟到调用闭包为止。如果查看lazy seq的源代码,您会注意到它是一个将其参数打包到函数体中的宏: user=> (source lazy-seq) (defmacro lazy-seq "Takes a body of express
lazy seq
如何按照本文档页面使尾部递归“安全”
如果您查看
lazy seq
的实现,您将看到它确实返回了一个闭包(一个保持上下文的函数):
因此,在
(cons n(正数(inc n))
中对正数的调用不会立即计算,而是延迟到调用闭包为止。如果查看lazy seq
的源代码,您会注意到它是一个将其参数打包到函数体中的宏:
user=> (source lazy-seq)
(defmacro lazy-seq
"Takes a body of expressions that returns an ISeq or nil, and yields
a Seqable object that will invoke the body only the first time seq
is called, and will cache the result and return it on all subsequent
seq calls. See also - realized?"
{:added "1.0"}
[& body]
(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
产生了这样的结果:
user=> (macroexpand '(lazy-seq (cons 1 (lazy-seq [2 3 4]))))
(new clojure.lang.LazySeq (fn* [] (cons 1 (lazy-seq [2 3 4]))))
这给了你一个正在发生的事情的提示:尾部位置的执行被推迟到需要时。这是如何实现的?请看:
这两个方法执行callable以获取尾部值——如果它们看到LazySeq,也会释放其中的值。然后缓存结果。在进行函数调用(在大多数语言中)时,会将一个新帧推送到调用堆栈上。如果这样做,则会重复生成无限的数字序列(递归或循环)您最终会溢出。通过使用延迟求值,您的递归次数仅与take
的参数相同@JaredSmith事实上,无论take
的参数是什么,您只向堆栈添加了一小部分恒定数量的帧。您自己很容易看出您的解释不起作用:尝试编写(最后一个)(以1e6(范围))为例)
,注意即使JVM没有足够的空间容纳一百万个堆栈帧,您仍然可以得到正确的答案。@amalloy我的观点是正确的。
user=> (source lazy-seq)
(defmacro lazy-seq
"Takes a body of expressions that returns an ISeq or nil, and yields
a Seqable object that will invoke the body only the first time seq
is called, and will cache the result and return it on all subsequent
seq calls. See also - realized?"
{:added "1.0"}
[& body]
(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
user=> (macroexpand '(lazy-seq (cons 1 (lazy-seq [2 3 4]))))
(new clojure.lang.LazySeq (fn* [] (cons 1 (lazy-seq [2 3 4]))))
final synchronized Object sval(){
if(fn != null)
{
sv = fn.invoke();
fn = null;
}
if(sv != null)
return sv;
return s;
}
final synchronized public ISeq seq(){
sval();
if(sv != null)
{
Object ls = sv;
sv = null;
while(ls instanceof LazySeq)
{
ls = ((LazySeq)ls).sval();
}
s = RT.seq(ls);
}
return s;
}