关于Clojure的懒惰

时间:2015-02-06 13:59:20

标签: clojure

我对clojure的懒惰序列感到好奇。在REPL中,我定义了一个变量 foo

user> (def foo (map println [1 2 3]))
#'user/foo

在评估 foo 第一次时,它似乎有效:

user> foo
1
2
3
(nil nil nil)

但是在第一次之后,为什么它会变得懒惰?

user> foo
(nil nil nil)

4 个答案:

答案 0 :(得分:3)

println不是函数,您在第一次评估foo时看到的是{em>副作用 { {1}}。当您第二次评估println时,foo未再次调用,因为println的结果是已缓存

您可以看到(map println [1 2 3])是懒惰的,因为当您定义map时,控制台中不会打印任何内容。只有在评估foo时才会打印出来。

请参阅Laziness in Clojure

如果您使用功能,例如foo

inc

结果始终相同,没有任何副作用。 Clojure中的mapfilteretc旨在与函数一起使用,但该语言并不禁止您将它们与副作用。例如,在Haskell中,您甚至无法编写等效表达式,代码将无法编译。

答案 1 :(得分:2)

收藏集值。 println返回的值为nilprintln的副作用是让您的屏幕上显示某些内容。

映射println创建的值存储在var中。这是一个nil值的惰性序列,由println返回。

答案 2 :(得分:1)

只是详细说明你的问题。 println仅对*out*流产生副作用,默认情况下绑定到标准输出。

您可以同时从map函数返回打印和某些值,例如

user> (defn print-and-inc [n]
        (do
          (println "called with n= " n)
          (inc n)))
#'user/print-and-inc   

do将按顺序执行每个表达式,并在这种情况下返回最后一个(inc n)的结果。 如果您现在将foo定义为print-and-incvector int的{​​{1}}的映射

user> (def foo (map print-and-inc [1 2 3 4 5]))
#'user/foo
user> 
user> foo
called with n=  1
called with n=  2
called with n=  3
called with n=  4
called with n=  5
(2 3 4 5 6)
user> 
user> foo
(2 3 4 5 6)

你看到map的懒惰,因为打印只在第一次调用foo时发生。但现在foo保存的结果是初始集合的递增值。

注意:这可用于将信息记录/跟踪到您的代码中,但有一个标准库tools.logging

答案 3 :(得分:1)

除了其他人指出的内容之外,请注意在repl中尝试懒惰是有点问题的。懒惰的序列实际上没有值,直到通过某些使用该值的操作实现它们。在打印结果时,repl有一个隐式的doall来执行此操作。这意味着序列通常在您在repl中使用时实现,但在您的实际代码中使用它时可能不会。当您运行代码时,会得到意外的结果,因为序列尚未在您预期的位置实现,因为尚未调用repl隐式doall。作为一个如何引起混淆的例子,请看http://nicksellen.co.uk/2013/10/26/clojure-lazy-repl.html