【问题标题】:Confused about evaluation of lazy sequences对惰性序列的评估感到困惑
【发布时间】:2021-12-07 10:13:53
【问题描述】:

我正在试验 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

因此,它似乎评估了所有项目,而不是只评估第一项,即使我只访问第一项。

如果惰性序列的状态只能是所有计算值和没有计算值,那么它如何获得惰性求值的优势?我来自计划背景,我期待更像流的行为。看来我弄错了。谁能解释一下是怎么回事?

【问题讨论】:

    标签: functional-programming clojure lazy-evaluation


    【解决方案1】:

    懒惰不是全有或全无,但 seq 的一些实现对输入序列的“块”进行操作(有关说明,请参阅 here)。这是矢量的情况,您可以使用chunked-seq? 进行测试:

    (chunked-seq? (seq [1 2 3 4 5]))
    

    当给定集合 map checks to see 时,如果底层序列是分块的,如果是这样,则在每个块的基础上评估结果,而不是一次评估一个元素。

    块大小通常为 32,因此您可以通过比较结果来查看此行为

    (first (map square (vec (range 35))))
    

    这应该只显示前 32 个项目的消息,而不是整个序列。

    【讨论】:

    • 性能是否是这种偏离惰性 eval 的 just-evaluate-enough 原则的唯一原因?
    • @Lee 我想知道为什么你链接的博客中的 sseq1 函数还没有在官方库中。控制一个序列的块大小会很好(例如,如果每个调用都发出一个 http 请求,最好让它一次执行 1 个,一次执行 32 个会消耗大量资源)。跨度>
    猜你喜欢
    • 1970-01-01
    • 2014-04-26
    • 2022-08-16
    • 2015-08-14
    • 1970-01-01
    • 2018-03-12
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多