(require '[clojure.core.reducers :as r])
(r/fold + (r/filter even? (r/map inc [1 1 1 2])))
;=> 6
歸約器提供一種使用 序列 來處理標準 Clojure 集合的替代方法。序列函式通常會以惰性方式依序套用,建立中間結果,而且只會使用單一執行緒。然而,許多序列函式(例如 map 和 filter)在概念上可以並行套用,產生會隨著機器核心數增加而自動加速的程式碼。如需有關歸約器原理的更多詳細資訊,請參閱原始 部落格 文章。
Reducer 是可縮減集合(知道如何縮減自己的集合)與縮減函數(縮減過程中需要執行的「配方」)的組合。標準序列操作會替換成不會執行操作的新版本,而僅轉換縮減函數。操作的執行會延遲到執行最後的縮減。這會移除序列中出現的中間結果和延遲求值。
此外,有些集合(持續向量和映射)是可摺疊的。Reducer 上的摺疊操作會透過下列方式平行執行縮減:
以指定的粒度分割可縮減集合(預設 = 512 個元素)
將縮減套用至每個分割區
使用 Java 的 fork/join 架構遞迴地結合每個分割區。
如果集合不支援摺疊,它會改為使用非平行縮減。
clojure.core.reducers 名稱空間(在此別名為 r)提供一個備用的 r/reduce 函數。
(r/reduce f coll)
(r/reduce f init coll)
Reducer 版本的差異在於
映射 coll 會使用 reduce-kv 縮減
當未提供 init 時,f 會在沒有參數的情況下呼叫,以產生一個識別值
注意:f 可能會被呼叫多次以提供識別值
一般來說,大多數使用者不會直接呼叫 r/reduce,而應該優先使用 r/fold,它實作了平行縮減和結合。不過,執行急切縮減並減少中間結果可能是很有用的。
(r/fold reducef coll)
(r/fold combinef reducef coll)
(r/fold n combinef reducef coll)
r/fold 會取得一個可縮減集合,並將其分割成大約 n(預設為 512)個元素的群組。每個群組會使用 reducef 函數縮減。reducef 函數會在沒有參數的情況下呼叫,以在每個分割區產生一個識別值。然後,這些縮減的結果會使用 combinef(預設為 reducef)函數縮減。當在沒有參數的情況下呼叫時,(combinef) 必須產生其識別元素 - 這會被呼叫多次。操作可以平行執行。結果會保留順序。
下列函數(類似於序列版本)從可還原或可摺疊集合建立還原器: r/map r/mapcat r/filter r/remove r/flatten r/take-while r/take 和 r/drop。這些函數都不會實際轉換來源集合。若要產生累積結果,您必須使用 r/reduce 或 r/fold。若要產生輸出集合,請使用 clojure.core/into 選擇集合類型或提供的 r/foldcat 來產生可還原、可摺疊、可序列化且可計數的集合。
使用 fold 以 + 進行加總
(require '[clojure.core.reducers :as r])
(r/fold + (r/filter even? (r/map inc [1 1 1 2])))
;=> 6
使用 into 產生最終集合
(into [] (r/filter even? (r/map inc (range 100000))))
(r/foldcat (r/filter even? (r/map inc (range 100000))))
使用 fold 指定還原函數和組合函數
(defn count-words
([] {})
([freqs word]
(assoc freqs word (inc (get freqs word 0)))))
(defn merge-counts
([] {})
([& m] (apply merge-with + m)))
(defn word-frequency [text]
(r/fold merge-counts count-words (clojure.string/split text #"\s+")))