您能否以更广泛和清晰的方式解释,lazy-seq
如何根据此文档页面使尾递归“安全”?
https://clojuredocs.org/clojure.core/lazy-seq
;; The following defines a lazy-seq of all positive numbers. Note that
;; the lazy-seq allows us to make a recursive call in a safe way because
;; the call does not happen immediately but instead creates a closure.
user=> (defn positive-numbers
([] (positive-numbers 1))
([n] (lazy-seq (cons n (positive-numbers (inc n))))))
#'user/positive-numbers
user=> (take 5 (positive-numbers))
(1 2 3 4 5)
答案 0 :(得分:1)
如果你看一下lazy-seq
的实现,你会发现它确实会返回一个闭包(一个保持上下文的函数):
(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body))
因此,positive-numbers
中对(cons n (positive-numbers (inc n))
的调用不会立即进行评估,而是会延迟直到调用闭包。
答案 1 :(得分:1)
如果你看一下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]))))
这会给你一个暗示正在发生的事情:尾部位置的执行被推迟到需要时。这是如何实现的?看看clojure/lang/LazySeq.java (Copyright (c) Rich Hickey. All rights reserved):
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;
}
这两个方法执行callable以获取尾值 - 如果他们看到一个,它们也会解除包含的LazySeq。然后缓存结果。