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
by
我认为我现在明白这里发生了什么

(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 因此懒序列不会计算
第二次打印查找 cnt' 组织太懒会被计算并且 @cnt 被更新
第三次打印只是显示更新后的 @cnt

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