2015-02-06 91 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