2012-05-28 25 views
6

我寫道:Lazyness和計算器

(fn r [f xs] 
    (lazy-seq 
    (if (empty? xs) 
    '() 
    (cons (f (first xs)) (r f (rest xs)))))) 

解決4clojure.com的問題#118:http://www.4clojure.com/problem/118

它要求重新實現地圖而不使用地圖等和溶液流測試(我不知道它是否正確:與其他解決方案非常接近)。

因爲這個問題指出,它必須是我懶的​​懶-SEQ寫了上面的代碼通過「包裝」我的解決辦法......但是我不知道如何懶惰序列作品。

我不明白什麼是「懶惰」在這裏,也不知道我如何測試它。

當我問(type ...)我得到的,勿庸置疑,一個clojure.lang.LazySeq,但我不知道那是什麼,什麼我得到的,如果我只是刪除懶序列「包裝」之間的區別。

現在當然,如果我刪除懶惰序列我得到一個計算器,爲什麼試圖執行此:

(= [(int 1e6) (int (inc 1e6))] 
    (->> (... inc (range)) 
     (drop (dec 1e6)) 
     (take 2))) 

否則(即:如果我讓到位懶-seq的包裝),它似乎工作正常。

所以我決定嘗試以某種方式「調試」/跟蹤正在發生的事情,試圖瞭解它是如何工作的。我把下面的宏(我發現SO IIRC):

(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#)) 

包裹內DBG宏工作版本,並試圖再次執行。現在kaboom:現在工作正常的版本現在也拋出了一個stackoverflow。

現在我不確定:也許這是宏的不利影響,它會以某種方式強制對否則不會被評估的東西進行評估?

這將是巨大的,如果任何人都可以解釋,用這個簡單的功能和簡單的測試,lazyness沒有怎麼在這裏工作,究竟是什麼被調用的時候,等

+0

Clojure有'實現?'測試函數來測試是否已實現惰性序列的值。例如(實現?(範圍))將是錯誤的。 – NielsK

回答

4

整個神奇之處在於clojure.lang.LazySeq Java類。它本身實現了對lazy-seq宏的ISeq接口和s-expressions參數,它們被轉換爲一個沒有任何參數的函數,並被傳遞給clojure.lang.LazySeq的構造函數(以IFn對象爲參數的構造函數),並且因爲in您再次調用r函數的結束(返回ISeq而不是完整列表),這使得LazySeq可以懶惰地評估項目。

所以基本上流程是這樣的:

  • LazySeq調用FN傳遞給它(我。e代碼的其餘部分)
  • 這個Fn調用返回一個ISeq,因爲列表實現了ISeq。由於遞歸調用r,這將返回ISeq(list)作爲具體值的第一個值,其次是LazySeq對象。這個返回的ISeq存儲在類中的一個局部變量中。
  • LazySeq在調用next item時的ISeq實現在上面的步驟中調用了存儲在本地類變量中的下一個ISeq(list),並檢查它是否爲LazySeq類型(由於r,它將在第二項中)調用),如果它是LazySeq然後評估並返回然後項目,否則直接返回該項目(第一個具體值,你傳遞給缺點)

我知道這是一個小心靈彎曲的東西:)。我也剛剛瀏覽了Java代碼,並且在意識到這個魔術是可能的之後就能夠弄清楚了,因爲遞歸調用r本身會返回一個惰性序列。所以你有它,類定製分隔延續:)

+0

澄清這個習語中沒有涉及遞歸可能是有幫助的,因爲'r'的返回值是一個'LazySeq',只有在頭部實現時再次調用'r'。實現總是在'r'已經返回之後發生。 –