我正在试验clojure的惰性序列。为了查看什么时候会对某项求值,我创建了一个名为square的函数,它在返回结果之前打印结果。然后使用map将此函数应用于一个向量。
(defn square [x]
(let [result (* x x)]
(println "printing " result)
result))
(def s (map square [1 2 3 4 5])) ; outputs nothing
在我的s声明中,REPL不输出任何东西。这表示计算尚未开始。这似乎是正确的。然后:
(first s)
函数"first"只取第一项。所以我预计只有1会被求值。我的期望是REPL将输出以下内容:
printing 1
1
但是,REPL输出了以下内容:
printing 1
printing 4
printing 9
printing 16
printing 25
1
因此,它不是只计算第一个项目,而是计算所有项目,即使我只访问第一个项目。
如果一个惰性序列的状态只能是全值计算和不值计算,那么它如何获得惰性求值的优势?我有一个方案背景,我期望更像流的行为。看来我错了。有人能解释一下这是怎么回事吗?
惰性不是全有或全无,但是seq的一些实现对输入序列的'块'进行操作(参见这里的解释)。这是矢量的情况,你可以用chunked-seq?
测试:
(chunked-seq? (seq [1 2 3 4 5]))
当给定一个集合时,map
检查底层序列是否为块,如果是,则以每个块为基础计算结果,而不是一次计算一个元素。
块大小通常为32,因此通过比较
的结果可以看到这种行为。(first (map square (vec (range 35))))
这应该只显示前32项的消息,而不是整个序列。