fold(フォールド)inject(インジェクト)

このページでは、コレクションパイプラインパターンにおける操作について説明します。詳細なコンテキストについては、以下をご覧ください。

リデュース

提供された関数を使用して、入力要素を組み合わせます。多くの場合、単一の出力値にまとめられます。

reduce操作を理解するのが難しいと感じている人、あるいは避けている人にしばしば出会います。しかし、reduceはコレクション内の多くの項目から値を組み合わせるための貴重な操作です。

基本から始めましょう。reduce操作は、2つの引数(アキュムレータと現在のイテレーション)を取る関数を取ります。各イテレーションで、これらの2つの引数を単一の値に結合し、次のイテレーションで使用するためにアキュムレータに格納します。文字列のリストを連結するための次のコードを考えてみましょう。

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

何が起こるかを理解するために、イテレーションの各ステップを表に示します。

インデックス アキュムレータ(acc) 文字列(s) 結果
0 ”” “a” ”” + “a” “a”
1 “a” “b” “a” + “b” “ab”
2 “ab” “c” “ab” + “c” “abc”

最初の例では初期値を含めましたが、通常は必要ありません。初期値を省略すると、reduceは初期値を最初の要素に設定し、2番目の要素からイテレーションを開始します。

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

rubyではコレクションパイプライン操作にラムダ式を使用するのが習慣ですが、reduceの場合は関数に名前を付ける方が好きです。

これまでに示したreduce操作は、リストを左から反復処理するため、左reduceです。一部の言語には、右から左へ進む右reduceもあります。意味的には、これはリストを反転してから左reduceを実行するのと同じです。実装方法によっては、右reduce操作はメモリ、特にスタックの消費と遅延性に異なる影響を与える可能性があります。「fold」という用語を使用する場合、左foldはしばしば「foldl」、右foldは「foldr」と表記されます。

アキュムレータを使用した左右のfoldの概念は、reduceについて考える最も一般的な方法ですが、欠点があります。本質的に並列化できないことです。一部のアプローチでは、結合関数が結合法則である場合(clojureのreduceライブラリなど)、reduceを本質的に並列操作と見なすことでこれを解決しようとしています。

Smalltalkでのreduceの用語はinject:intoで、次のように使用されます。

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

Smalltalkの文字列連結演算子は " , " です。

Rubyは「inject」を「reduce」のエイリアスとして使用しますが、inject:into:のキーワードパラメータがないと、読みやすさが低下します。慣れ親しんでいたため使用していましたが、現在は「reduce」を使用することをお勧めします。