clojure中惰性序列的使用

The usage of lazy-sequences in clojure

我想知道 lazy-seq returns 是有限列表还是无限列表。有个例子,

(defn integers [n]
    (cons n (lazy-seq (integers (inc n)))))

当我运行喜欢

(first integers 10)

(take 5 (integers 10))

结果是 10 和 (10 11 12 13 14) .然而,当我 运行

(integers 10)

进程无法打印任何内容且无法继续。有没有人可以告诉我为什么以及laza-seq的用法。非常感谢!

它没有返回任何东西,因为你的整数函数创建了一个无限循环。

(defn integers [n]
  (do (prn n)
      (cons n (lazy-seq (integers (inc n))))))

(integers 10) 调用它,您会看到它永远计数。

当你尝试打印一个无界惰性序列时,它将完全实现,除非你限制*print-length*

当你说你是 运行

(integers 10)

你真正在做的是这样的:

user> (integers 10)

换句话说,您正在 REPL 中评估该表单(读取-评估-打印-循环)。

"read" 步骤将从字符串 "(integers 10)" 转换为列表 (integers 10)。非常简单。

"eval" 步骤将在周围的上下文中查找 integers,查看它是否绑定到一个函数,并使用参数 10:[=29 评估该函数=]

(cons 10 (lazy-seq (integers (inc 10))))

由于 lazy-seq 在需要时才被实现,简单地评估这种形式将产生一个 clojure.lang.Cons 对象,其 first 元素是 10 并且其 rest 元素是尚未实现的 clojure.lang.LazySeq

你可以用一个简单的def(没有无限挂起)来验证这一点:

user> (def my-integers (integers 10))
;=> #'user/my-integers

在最后的 "print" 步骤中,Clojure 主要尝试将它刚刚评估的表单结果转换为字符串,然后将该字符串打印到控制台。对于有限序列,这很容易。它只是不断地从序列中取出项目,直到没有任何剩余,将每个项目转换为一个字符串,用空格分隔它们,在末端加上一些括号,然后瞧瞧:

user> (take 5 (integers 10))
;=> (10 11 12 13 14)

但是正如您所定义的 integers,不会有一个点没有剩余的项目(好吧,至少在您得到整数溢出之前,但这可以通过使用来补救inc' 而不仅仅是 inc)。所以 Clojure 能够很好地读取和评估您的输入,但它根本无法打印无限结果的所有项目。

lazy-seq从不构造一个有限或无限的列表。它构造了一个 clojure.lang.LazySeq 对象。这是一个标称序列,它包装了一个没有参数的函数(通常称为 thunk),当被调用时 ;但是直到必须调用它时才会调用它,这就是该机制的目的:延迟评估实际序列。

因此您可以将无穷无尽的序列作为已评估 LazySeq 对象传递,前提是您永远不会实现它们。您在 REPL 的评估会引发实现,这是一个无休止的过程。