我对clojure的懒惰序列感到好奇。在REPL,我定义的变量富:关于Clojure的懒惰
user> (def foo (map println [1 2 3]))
#'user/foo
在第一时间来评估富,似乎工作:
user> foo
1
2
3
(nil nil nil)
但第一时间后,为什么它变得懒惰?
user> foo
(nil nil nil)
我对clojure的懒惰序列感到好奇。在REPL,我定义的变量富:关于Clojure的懒惰
user> (def foo (map println [1 2 3]))
#'user/foo
在第一时间来评估富,似乎工作:
user> foo
1
2
3
(nil nil nil)
但第一时间后,为什么它变得懒惰?
user> foo
(nil nil nil)
println
不是纯功能,你评估你看到在第一时间什么foo
是副作用println
。当您第二次评估foo
时println
不会再次被调用,因为(map println [1 2 3])
的结果是缓存。
而且你可以看到map
是懒惰的,因为当你定义了foo
时,控制台中没有打印任何内容。只有在评估foo
时才会打印。请参阅Laziness in Clojure。
如果使用纯功能像inc
:
(def foo (map inc [1 2 3]))
> foo
(2 3 4)
> foo
(2 3 4)
结果始终没有任何副作用相同。 map
,filter
,etc在Clojure中被设计为与纯粹使用功能,但语言不禁止您使用它们的功能与副作用。在Haskell中,例如,你甚至不能编写等价的表达式,代码将不能编译。
集合保存值。 println
返回的值为nil
。 println
的副作用是在屏幕上显示某些内容。
由映射println
创建的值存储在您的var。这是nil
值的一个延迟seq,它由println
返回。
只是为了详细说明你的问题。 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-inc
映射在int
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)
的vector
而且你看到的map
的lazyness,由于印刷时仅在第一次foo
被调用。但是现在foo
保存的结果是初始集合的递增值。
注:这可以用来记录/跟踪的相关信息到你的代码,但是有一个标准库tools.logging
除了被别人什么已经指出,需要注意的是,REPL内懒惰试验是有点问题。惰性序列实际上并没有值,除非它们通过使用该值的某个操作来实现。在打印结果时,repl有一个隐含的doall来做这件事。这意味着序列通常在您在repl中使用它时实现,但在实际代码中使用时可能不会。当你运行你的代码时,你会得到一个意外的结果,因为序列还没有在你期望的地方实现,因为repl隐式doall没有被调用。举一个例子说明这可能导致混乱的时刻,看看http://nicksellen.co.uk/2013/10/26/clojure-lazy-repl.html
这篇文章对于新的clojure开发者来说非常重要。非常感谢你! – 2015-02-11 20:57:31
因此,(map println [1 2 3])的结果在第一次运行后被缓存;缓存的结果不是序列本身,而是一个seqal对象。我的理解是正确的吗? – 2015-02-06 17:07:10
结果是一个'seqable',它包含将'println'应用于序列'[1 2 3]''的每个元素的结果。但'println'返回'nil',所以'map'返回的'seqable'是'(零零)“。 – 2015-02-06 17:16:23