2016-05-13 125 views
4

減少在這本書中Clojure for the Brave and True在覆蓋reduce該節結束時有一個挑戰:如何實現地圖使用Clojure中

如果你想鍛鍊真的會吹你的頭髮回來,請嘗試使用實施mapreduce

事實證明,這比我想象的要難得多(至少對我來說,是一個Clojure初學者)。幾個小時後,我想出了這個:

(defn map-as-reduce 
    [f coll] 
    (reduce #(cons (f %2) %1) '() (reverse coll))) 

是一個更好的方法來做到這一點?我特別感到沮喪的是,爲了正確工作,我必須將輸入集合反轉。看起來好像不太好!

回答

5

請記住,可以在一個矢量的末端有效地插入:

(defn map' [f coll] 
    (reduce #(conj %1 (f %2)) [] coll)) 

實施例:

(map' inc [1 2 3]) 
;=> [2 3 4] 

一個此map'和原始map之間的區別是,原始map返回ISeq而不只是一個Seqable

(seq? (map inc [1 2 3])) 
;=> true 

(seq? (map' inc [1 2 3])) 
;=> false 

您也可以用seq構成上述實施map'解決這個問題:

(defn map' [f coll] 
    (seq (reduce #(conj %1 (f %2)) [] coll))) 

最重要的區別是現在,雖然原來map很懶,這map'渴望,因爲reduce渴望。

1

您可以使用conj將追加到一個載體,而不是預先考慮到一個列表:

(defn my-map [f coll] 
    (reduce (fn [result item] 
       (conj result (f item))) 
      [] coll)) 

(my-map inc [1 2 3]) => [2 3 4] 
2

這是比較常見的扭轉結果,而不是輸入。以遞歸方式處理單鏈表時,這是一個常見的習慣用法。它保留了這個數據結構的線性複雜性。

您可能想要爲其他seq s提供不同的實現,例如,載體,可能基於conj而不是cons

我不會太擔心優雅與這種運動。

+1

我不明白爲什麼它會改變結果而不是輸入;複雜性是線性的。 –

+0

@SamEstep:是的,好的。扭轉結果更爲常見。 – Svante

+0

我不太瞭解你對「爲其他'seq's提供其他實現」的評論;本頁面上所有'map'的實現都適用於所有'Seqable'輸入。如果你的意思是*生成*不同類型的序列,那麼當我們很容易用['vec'](http)組成'map'時, ://clojuredocs.org/clojure.core/vec)等等。 –

5

只是爲了好玩: 地圖確實接受多個集合作爲參數。這裏是一個擴展的實現:

(defn map-with-reduce 
    ([f coll] (seq (reduce #(conj %1 (f %2)) [] coll))) 
    ([f coll & colls] 
    (let [colls (cons coll colls)] 
     (map-with-reduce (partial apply f) 
         (partition (count colls) 
            (apply interleave colls)))))) 
在REPL

user> (map-with-reduce inc [1 2 3]) 
(2 3 4) 

user> (map-with-reduce + [1 2 3] [4 5] [6 7 8]) 
(11 14) 
0

因爲它已經指出。您不必扭轉輸入。 cons在序列的開始處添加一個項目(甚至在矢量上),而conj將始終以最自然的方式添加,它總是以最快的方式添加項目以便進行收集。它將從左到右添加列表,從左到右添加矢量。

我注意到,大多數建議答案使用「減少」,所以允許我提出這一個主要使用遞歸:

(defn my-map [f coll] 
(loop [f f coll coll acc []] 
    (if (empty? coll) 
    acc 
    (recur f (rest coll) (conj acc (f (first coll))))))) 
+0

這個問題明確提到使用'reduce',所以儘管有趣,但這個答案決不會回答這個問題。 – mluisbrown

+0

@mluisbrown這是真的。我一定錯過了它明確表示使用'減少' – Lewix

3

真正的地圖調用序列在其收集的參數(S),並返回一個懶惰的序列,所以也許這是爲了讓它更接近真實地圖?

(defn my-map 
    [f coll] 
    (lazy-seq (reduce #(conj %1 (f %2)) [] (seq coll)))) 

我會補充說,作爲評論,但我沒有聲望。 :)