2015-02-06 75 views
1

我对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) 

回答

3

println不是功能,你评估你看到在第一时间什么foo副作用println。当您第二次评估fooprintln不会再次被调用,因为(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中,例如,你甚至不能编写等价的表达式,代码将不能编译。

+0

因此,(map println [1 2 3])的结果在第一次运行后被缓存;缓存的结果不是序列本身,而是一个seqal对象。我的理解是正确的吗? – 2015-02-06 17:07:10

+1

结果是一个'seqable',它包含将'println'应用于序列'[1 2 3]''的每个元素的结果。但'println'返回'nil',所以'map'返回的'seqable'是'(零零)“。 – 2015-02-06 17:16:23

2

集合保存值。 println返回的值为nilprintln的副作用是在屏幕上显示某些内容。

由映射println创建的值存储在您的var。这是nil值的一个延迟seq,它由println返回。

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-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

1

除了被别人什么已经指出,需要注意的是,REPL内懒惰试验是有点问题。惰性序列实际上并没有值,除非它们通过使用该值的某个操作来实现。在打印结果时,repl有一个隐含的doall来做这件事。这意味着序列通常在您在repl中使用它时实现,但在实际代码中使用时可能不会。当你运行你的代码时,你会得到一个意外的结果,因为序列还没有在你期望的地方实现,因为repl隐式doall没有被调用。举一个例子说明这可能导致混乱的时刻,看看http://nicksellen.co.uk/2013/10/26/clojure-lazy-repl.html

+0

这篇文章对于新的clojure开发者来说非常重要。非常感谢你! – 2015-02-11 20:57:31