2024 Clojure 状态调查! 中分享你的想法。

欢迎!请查看 关于 页面以获取更多关于此如何工作的信息。

0
语法和读取器

嗨,

(def cnt (atom 0))
(defn up[] (swap! cnt #(inc %)))
(defn down[] (swap! cnt #(dec %)))
(defn shift-by[offset] (swap! cnt #(+ % offset)))
(dotimes [_ 10] (up))
(println "cnt up => " @cnt)
(loop [i 0] (if (< i 10) (do (down)(recur (inc i)))))
(println "cnt down => " @cnt)
(def cnt' (for[i (range 5)] (shift-by i)))
(println "cnt shift1 => " @cnt)
(println "cnt' => " cnt')
(println "cnt shift2 => " @cnt)

cnt up => 10
cnt down => 0
cnt shift1 => 0
cnt' => (0 1 3 6 10)
cnt shift2 => 10

为什么 shift1 显示 0 而 shift2 显示 10?

祝愿商祺,
Daniel

1 答案

0

我的第一反应可能是 for 的延迟性。尝试 (def cnt' (vec (for[i (range 5)] (shift-by i)))) 看看是否有变化。

是的,这确实有变化。

(doall ...) 也一样

我认为我用 atom 是为了让延迟表达式强制评估
打印序列(cnt')是实现由for产生的懒序列的方法。
>>>打印序列(cnt')是实现由for产生的懒序列的方法。

是的,那么为什么Clojure不等待cnt'的计算并打印0呢?
因为`for`返回(计算为)一个懒序列,这意味着它将只在被消费时实现单个元素——这只是一个粗略的近似,但大致就是Clojure懒性的内容。懒性的一个原因就是不会完全实现整个(可能是无限的)序列,所以生成懒序列的操作(如for)可以快速返回。

所以cnt'的值是一个懒序列。当您打印它时,打印机会看到它是一个序列,所以它会迭代其元素,这将导致它们被实现,触发实现元素的代码中的副作用。

vec生成一个不是懒性的向量,所以它将需要在cnt'中的所有元素,这也会导致它们被实现。doall专门用于实现所有元素。

为了回答“为什么Clojure不等待cnt'进行计算”的问题——它确实在等待它,但我上面写的那样,cnt'计算为懒序列,这并不能保证其任何元素已经被实现。

我认为懒效果是一种不良习惯 https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
我现在觉得我明白了这里发生了什么

(def cnt (atom 0))
(def cnt' (for[i (range 5)] (shift-by i)))
(println "cnt shift1 => " @cnt)
(println "cnt' => " cnt')
(println "cnt shift2 => " @cnt)

cnt' 是懒的,好吧
第一次打印尝试访问 @cnt,因此 lazy 序列没有被评估
第二次打印尝试访问 cnt',lazy 序列被评估并且 @cnt 被更新
第三次打印仅显示更新后的 @cnt

更新 @cnt 是评估的副作用(可能是打印或写入文件)
我觉得在这里 clojure 做得是对的
...