无限递归惰性序列在 Clojure 中显示为空序列

Yiz*_*Sun 3 clojure

假设我写道:

(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)

当调用构造函数的fnarg 时clojure.lang.LazySeq,它返回与已经实现的相同的惰性序列。因此,当您尝试将惰性序列打印到 REPL 时,迭代会立即终止并返回 nil。

您可以验证的类型stuffclojure.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)