mor*_*ode 5 functional-programming clojure lazy-evaluation
我正在尝试 Clojure 的惰性序列。为了查看某个项目的评估何时发生,我创建了一个名为 square 的函数,该函数在返回结果之前打印结果。然后我使用地图将此函数应用于向量。
(defn square [x]
(let [result (* x x)]
(println "printing " result)
result))
(def s (map square [1 2 3 4 5])) ; outputs nothing
Run Code Online (Sandbox Code Playgroud)
在我的 s 声明中,REPL 不输出任何内容。这表明计算尚未开始。这似乎是正确的。然后我这样做:
(first s)
Run Code Online (Sandbox Code Playgroud)
函数“first”仅采用第一项。所以我预计只会评估 1 个。我的期望是 REPL 将输出以下内容:
printing 1
1
Run Code Online (Sandbox Code Playgroud)
然而,REPL 输出了以下内容。
printing 1
printing 4
printing 9
printing 16
printing 25
1
Run Code Online (Sandbox Code Playgroud)
因此,它似乎不是只评估第一个项目,而是评估所有项目,即使我只访问第一个项目。
如果惰性序列的状态只能是计算所有值和不计算值,那么它如何获得惰性求值的优势呢?我来自计划背景,我期待更像流的行为。看来我错了。谁能解释一下发生了什么事吗?
惰性并不是全有或全无,但 seq 的某些实现对输入序列的“块”进行操作(请参阅此处以获取解释)。这是向量的情况,您可以使用以下命令进行测试chunked-seq?:
(chunked-seq? (seq [1 2 3 4 5]))
Run Code Online (Sandbox Code Playgroud)
当给定一个集合时,map 检查底层序列是否被分块,如果是,则基于每个块而不是一次评估一个元素。
块大小通常为 32,因此您可以通过比较结果来看到此行为
(first (map square (vec (range 35))))
Run Code Online (Sandbox Code Playgroud)
这应该只显示前 32 个项目的消息,而不是整个序列。