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'求值”——Clojure确实在等待它,但如我上面所写的,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,因此不会导出懒序列的求值。
第二次打印用于cnt',并评估懒惰序列并更新@cnt
第三次打印只显示更新的@cnt

更新@cnt是评估的副作用(可能是打印或写入文件)
我认为Clojure在这里做了正确的事情
...