foldinject

此頁面描述了收集管線模式中的操作。如需更多背景資訊,請閱讀

縮減

使用提供的函式來組合輸入元素,通常會組合成單一輸出值

我經常遇到一些人覺得 reduce 操作難以理解,甚至到避而遠之的地步。然而,reduce 是將收集中許多項目中的值組合起來的寶貴操作。

從基礎開始:還原操作會採用一個函式,該函式會採用兩個引數 - 累加器和目前的反覆運算。在每次反覆運算中,它會將這兩個引數組合成單一值,然後將其放入累加器中,供下一次反覆運算使用。考慮使用這段程式碼來串接字串清單

ruby…
["a", "b", "c"].reduce(""){|acc, s| acc + s}
# => "abc"
clojure…
(reduce str "" ["a" "b" "c"])
;; => "abc"

要了解發生了什麼事,以下是反覆運算中每一步驟的表格。

索引 累加器 s 表達式 結果
0 ”” “a” ”” + “a” “a”
1 “a” “b” “a” + “b” “ab”
2 “ab” “c” “ab” + “c” “abc”

對於第一個範例,我包含了一個初始值,但通常沒有必要。如果我省略初始值,則還原會將初始值設定為第一個元素,並從第二個元素開始反覆運算。

ruby…
["a", "b", "c"].reduce(:+)
# => "abc"
clojure…
(reduce str ["a" "b" "c"])
;; => "abc"

儘管我在 ruby 中的習慣是對收集管線操作使用 lambda,但我比較喜歡只為 reduce 命名函式。

到目前為止我展示的 reduce 操作都是左還原,因為我們從左邊開始反覆運算清單。有些語言也有從右到左進行的右還原。語義上,這與反轉清單然後進行左還原相同。根據其實現方式,右還原操作可能會對記憶體,特別是堆疊,消耗和惰性產生不同的影響。如果語言使用「fold」術語,則左折疊通常寫成「foldl」,右折疊寫成「foldr」。

使用累加器進行左右折疊的概念是思考 reduce 最常見的方式,但它有一個缺點 - 它本身不可並行化。一些方法會試圖將 reduce 視為一個本質上並行的操作來解決這個問題,前提是組合函式是關聯的(例如 clojure 的 reduce 函式庫)。

Smalltalk 對 reduce 的術語是 inject:into,會像這樣使用

aList inject: '' into: [:acc, :each| acc , each]

Smalltalk 的字串串接運算子是「 , 」.

Ruby 使用「inject」作為「reduce」的別名,但沒有 inject:into: 的兩個關鍵字參數,讀起來不太順暢。我還是因為習慣而使用它,但現在比較喜歡使用「reduce」.