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