假设我写道:
(def stuff
(lazy-seq stuff))
Run Code Online (Sandbox Code Playgroud)
当我stuff
在 REPL 中询问 的值时,我希望它陷入无限循环,因为我将stuff
其定义为它自己(这几乎完全没有说明这个序列)。
但是,我得到了一个空序列。
> stuff
()
Run Code Online (Sandbox Code Playgroud)
为什么?
编辑:“递归”是指递归数据,而不是递归函数。
我仍然对序列终止的原因感到困惑。作为比较,下面的代码被卡在无限循环(和吹堆)。
(def stuff
(lazy-seq (cons (first stuff) [])))
Run Code Online (Sandbox Code Playgroud)
一些背景:这个问题源于我试图使用 Eratosthenes 筛法实现素数生成器。我的第一次尝试是:
(def primes
(lazy-seq (cons 2
(remove (fn [x]
(let [ps (take-while #(< % x) primes)]
(some #(zero? (mod x %)) ps)))
(range 3 inf))))) ;; My customized range function that returns an infinite sequence
Run Code Online (Sandbox Code Playgroud)
我认为它永远不会奏效,因为take-while
即使它们还无法计算,也会不断要求更多的素数。所以当它运行得很好时,它让我感到惊讶。
> (take 20 primes)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71)
Run Code Online (Sandbox Code Playgroud)
小智 6
首先,每个lazy seq只能实现一次。其次,你的定义stuff
不使用递归——stuff
不是一个函数。如果你看一下定义的lazy-seq
,你可以看到你的定义stuff
扩展为
(def stuff (new clojure.lang.LazySeq (fn* [] stuff)))
Run Code Online (Sandbox Code Playgroud)
当调用构造函数的fn
arg 时clojure.lang.LazySeq
,它返回与已经实现的相同的惰性序列。因此,当您尝试将惰性序列打印到 REPL 时,迭代会立即终止并返回 nil。
您可以验证的类型stuff
是clojure.lang.LazySeq
user=> (type stuff)
clojure.lang.LazySeq
Run Code Online (Sandbox Code Playgroud)
并且打印stuff
到REPL后,stuff
已经实现
user=> (realized? stuff)
false
user=> stuff
()
user=> (realized? stuff)
true
Run Code Online (Sandbox Code Playgroud)
您可以使用递归来获得您预期的效果
user=> (defn stuff
[]
(lazy-seq (stuff)))
#'user/stuff
user=> (stuff) ;; Hangs forever.
Run Code Online (Sandbox Code Playgroud)