我是clojure的初学者,有一个困惑的问题:
(do (println "ok") (for [x [1 2 3]] (println x)))
输出:好吧123
我能理解,但是
(do (for [x [1 2 3]] (println x)) (println "ok"))
输出:好吧nil
为什么for函数没有被执行?
示例中的(println x)
形式是副作用,只有在实现for
惰性序列时才会发生。
下面是第一个重新格式化的例子:
(do
(println "ok")
(for [x [1 2 3]]
(println x)))
do
依次求值并返回最后一个表达式的值。这里的最后一个表达式是一个惰性序列。如果您在REPL中对其进行计算,则惰性序列将实现打印其元素(给定值(nil nil nil)
)并导致打印1
2
3
的副作用。
下面是第二个重新格式化的例子:
(do
(for [x [1 2 3]]
(println x))
(println "ok"))
再次,do
依次求值表达式。第一个是惰性序列,但它的值没有被使用,因此永远不会实现。这意味着不会出现打印1
2
3
的副作用。
简而言之:do宏计算所有参数并返回最后一个参数的值,这迫使惰性序列的元素实现。
我假设您在REPL中计算了两个表达式。
由于for宏,第一个表达式返回一个惰性序列。REPL强制对返回值进行求值(这样它就可以显示求值的结果),因此序列的每个元素都实现了打印到屏幕上的副作用。
在第二种情况下,返回值是nil
,因为do形式中的最后一个表达式是println调用。在这种情况下也创建了惰性序列,但它的元素永远不会实现,因为它的值永远不会被使用。
如果你想强制求值一个序列,你可以使用doall或dorun。如果对序列不感兴趣,只需要一个循环结构,可以使用doseq代替for。
其他答案已经提供了很好的背景。如果您需要for
的高级特性,但又不想要惰性方面,只需将整个内容包装在(vec ...)
表单中:
(vec (for [x [1 2 3]]
(println x)))
这已经在forv
宏中为您完成了,可以在这里看到。