对Clojure的懒惰评估发生了什么

时间:2012-06-28 23:57:42

标签: clojure lazy-evaluation

我正在扭曲我的旧java / python头部的clojure方式。请帮助我理解clojure的懒惰特征。

=> (def myvar (lazy-seq [1 2 (prn "abc")]))
#'user/myvar

以上很容易理解。由于它是一个懒惰的序列,(prn“abc”)将不会被评估,因此没有打印。

=> (def myvar (lazy-seq [1 2 (prn undefined-var)]))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: undefined-var in this context, compiling:(NO_SOURCE_PATH:1) 

如您所见,上述内容会引发错误。为什么?

我的(错误的)理解是,因为它是懒惰的,所以(prn undefined-var)可以合法地在这里,即使“undefined-var”尚未定义。

请任何人以正确的方式指出我的理解。

3 个答案:

答案 0 :(得分:9)

当clojure读者找到

 (def myvar (lazy-seq [1 2 (prn undefined-var)]))

它需要编译它,这就是它抛出错误的原因,因为未定义undefined-var。在第一种情况下,它编译好,但是在你使用seq之前它不会被执行。

答案 1 :(得分:8)

上述两个答案都为您提供了有关该主题的良好信息,但我将尝试在此处确定关键问题。当你在(+ x 2)这样的REPL上写s-expression时会发生两件事:

  1. the reader解析产生表单的字符,如符号,
  2. evaluation表格。
  3. 延迟评估会推迟第二步,但在第一步中,当读者遇到undefined-var时,它会尝试将其转换为符号,并发现此符号未定义。

答案 2 :(得分:1)

你似乎有正确的理解,只是这不是lazy-seq函数如何工作 这是一个典型的例子:

user> (lazy-seq (cons 4 (range 5)))
(4 0 1 2 3 4)

lazy-seq几乎总是在第一个参数中使用cons表达式 是序列中的第一项,第二个参数是代码 生成列表的其余部分。人们很少需要使用lazy-seq 直接在日常Clojure中使用map reducefilter等表单, 等等更常见。

用于产生序列其余部分的函数中的代码也是必需的 为了能够编译,在您的情况下,您也可以延迟编译 通过使用eval虽然我建议不要使用eval这样的事情 在代码中,其他人需要阅读;虽然很好学习; - )

user> (def myvar (lazy-seq (cons 1 (eval '(prn undefined-var))))) ; don't do this at work
#'user/myvar
user> myvar
; Evaluation aborted.
user>