4

我正在以 clojure 方式扭曲我的旧 java/python 头。请帮助我理解 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”尚未定义。

请任何人指出我对正确方法的理解。

4

3 回答 3

9

当 clojure 阅读器发现

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

它需要编译它,这就是它抛出错误的原因,因为未定义 undefined-var。在第一种情况下,它可以编译,但直到您使用 seq 才会执行。

于 2012-06-29T00:20:50.070 回答
8

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

  1. 由读者解析产生形式的字符,例如符号,
  2. 表格的评估

惰性求值推迟了第二步,但在第一步中,当读者遇到undefined-var它试图将其转换为符号时,发现没有定义这样的符号。

于 2012-06-29T09:22:00.573 回答
1

您似乎有正确的理解,这不是lazy-seq函数的工作方式 ,这里是一个典型的例子:

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

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

生成序列其余部分的函数中的代码也需要能够被编译,在您的情况下,您也可以通过使用延迟编译,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> 
于 2012-06-29T00:16:42.573 回答