2016-03-10 42 views
1

我是clojure的新手,目前正在與loop/recur拼搏。這個問題基本上是爲什麼我的'自定義'功能。不會返回一個懶惰的序列。我的實現有什麼問題,或者你不應該在這種情況下使用遞歸?用Clojure中的遞歸生成lazy seq?

(defn my-range 
    [nr] 
    (loop [l nr acc '()] 
    (if (< l 1) 
     acc 
     (recur (dec l) (conj acc l))))) 

當我運行它:

> (time (take 10 (my-range 100000))) ;; "Elapsed time: 85.443 msecs" 
> (time (take 10 (range 100000))) ;; "Elapsed time: 0.095 msecs" 

的非常大的時間差,讓我相信的列表中首先構造,然後取10。

+0

標準'range'從'0'開始,而不是'1'。這不會影響問題。 – Thumbnail

回答

11

您不要在my-range中使用任何惰性構造。既然你從最後開始整理列表,並開始工作,訪問前十個元素的唯一方法是首先實現所有其他元素。

懶惰序列從頭開始,向工作進行到底,就像這樣:

(defn my-range 
    ([end] 
    (my-range 1 end)) 
    ([start end] 
    (when (<= start end) 
    (lazy-seq (cons start (my-range (inc' start) end)))))) 

這裏的技巧是,你不返回一個實現序列。相反,您返回存儲此函數的對象:

#(cons start (my-range (inc' start) end)) 

當有人在該對象上調用seq,它會調用上述功能,緩存它的結果,並返回結果。未來呼叫seq將簡單地返回緩存的結果。但請注意,您傳遞給cons的第二個參數是也是一個惰性序列(因爲my-range的調用返回lazy-seq),所以它只會在必要時纔會實現。

只是爲了保持完整性,另一種方式來寫這個函數如下:

(defn my-range 
    [end] 
    (take end (iterate inc' 1))) 

這工作,因爲iteratetake都返回懶惰序列。

+0

非常感謝。憑藉我非常有限的clojure知識,我明白了。但是我不明白你爲什麼使用'''inc''''而不是'''inc'''? – skamsie

+2

嘗試'(inc Long/MAX_VALUE)'和'(inc'Long/MAX_VALUE)'。 –

+0

明白了,謝謝:) – skamsie