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 的评估会引发实现,这是一个无休止的过程。
我想知道 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 的评估会引发实现,这是一个无休止的过程。