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

What happened with lazy evaluation of Clojure

我正在扭旧的Java/Python头。请帮助我理解Clojure的懒惰特征。

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

以上内容很容易理解。因为它是一个懒惰的序列,所以(prn"abc")不会被评估,因此不会打印任何内容。

1
2
=> (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"还没有被定义。

请任何人指出我的理解是正确的。


当陪审员发现

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

它需要编译它,这就是它抛出错误的原因,因为未定义的var没有定义。在第一种情况下,它编译为OK,但只有在使用seq之后才会执行。


以上两个答案都为您提供了关于这个主题的很好的信息,但我将尝试在这里找出关键问题。当您在诸如(+ x 2)之类的repl上编写s-expression时,会发生两件事:

  • 由读者分析产生形式的特征,如符号,
  • 评估表格。
  • 懒惰的评价构成了第二步,但在第一步,当读者遇到undefined-var时,它试图将其转换为一个符号,并发现这种符号没有被定义。


    你似乎有正确的理解,只是这不是lazy seq函数的工作原理。下面是一个典型的例子:

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

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

    生成序列其余部分的函数中的代码也需要为了能够编译,在您的情况下,您也可以延迟编译使用eval,不过我建议不要使用eval在代码中,其他人需要阅读;但这有助于更好地学习;-)

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