8

今天我们在代码中偶然发现了一个问题,无法回答这个 Clojure 问题:

Clojure 是严格还是懒惰地评估不纯代码(或对 Java 代码的调用)?

似乎副作用 + 惰性序列会导致奇怪的行为。


以下是我们所知道的导致问题的原因:

Clojure 有惰性序列:

user=> (take 5 (range)) ; (range) returns an infinite list
(0 1 2 3 4)

而且 Clojure 有副作用和不纯的功能:

user=> (def value (println 5))
5                               ; 5 is printed out to screen
user=> value
nil                             ; 'value' is assigned nil

此外,Clojure 可以调用 Java 对象,这可能包括副作用。但是,副作用可能与惰性求值的交互作用很差:

user=> (def my-seq (map #(do (println %) %) (range)))
#'user/my-seq
user=> (take 5 my-seq)                               
(0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
0 1 2 3 4)

所以它返回了前 5 个元素,但打印了前 31 个!

我假设如果在 Java 对象上调用副作用方法,也会出现同样类型的问题。这可能会使推理代码和弄清楚会发生什么变得非常困难。


辅助问题:

  • 程序员是否应该注意和防止这种情况?(是的?)
  • 除了序列,Clojure 是否执行严格的评估?(是的?)
4

2 回答 2

8

Clojure 的惰性 seqs 块大约 30 个项目,因此进一步减少了少量开销。这不是纯粹主义者的选择,而是实用的选择。请参阅“Clojure 的乐趣”,了解一次实现一个元素的普通解决方案。

由于您遇到的原因,惰性序列不是不纯函数的完美匹配。

Clojure 也将严格评估,但使用宏时情况会有所不同。诸如此类的内置函数if自然会进行评估。

于 2011-10-19T16:18:58.110 回答
2

只要便于实现,无论在其中引用什么,都会或多或少地评估惰性结构。所以,是的,程序员需要小心并在需要时强制实现惰性序列。

我不知道您所说的严格评估是什么意思。

于 2011-10-19T17:12:22.120 回答