我正在扭曲我的旧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”尚未定义。
请任何人以正确的方式指出我的理解。
答案 0 :(得分:9)
当clojure读者找到
时 (def myvar (lazy-seq [1 2 (prn undefined-var)]))
它需要编译它,这就是它抛出错误的原因,因为未定义undefined-var。在第一种情况下,它编译好,但是在你使用seq之前它不会被执行。
答案 1 :(得分:8)
上述两个答案都为您提供了有关该主题的良好信息,但我将尝试在此处确定关键问题。当你在(+ x 2)
这样的REPL上写s-expression时会发生两件事:
延迟评估会推迟第二步,但在第一步中,当读者遇到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
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>