gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

Reduxのreducerはなんでreducerと呼ばれているのか

Reduxのreducerはなんでreducerなのか、そもそもreducerってなんだ、みたいなことを調べたので書きます。割と情弱っぷりを晒しています。間違ったこと書いてたら直します。

背景

Reduxでは、reducerってやつが状態の変更方法を知っています。reducerはReduxのドキュメントに以下のように書いてある通り、

状態とアクションを受け取って、次の状態を返すやつです。

しかし、reducerという言葉はどういう意味なのでしょう。「状態とアクションを受け取って、次の状態を返すやつ」を、なぜreducerと呼ぶのでしょう。

Weblioの辞書で調べると、

〔+目的語+to+(代)名詞〕〈ものを〉(整理して)〔簡単な形に〕変える,まとめる.

という意味が出てきます。上述の通りReduxのreducerはstateを変えたものを返すやつなので、これが適切なのではないでしょうか。

ここで納得して終わりにしてもいいのですが、Reduxのreducerの意味がこれでよしとするには少し早いような気がします。Reduxの文脈で、なぜreducerという言葉が使われているのかをもう少し調べてみます。

Why is it called a reducer?

Google検索で、雑に why reducerと検索すると、先程のページが出てきます。

疑問に対する答えに当たる部分はここでしょうか。

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

ふむ。Array.prototype.reduce(reducer, ?initialValue)として渡せるような関数だからreducerと呼ばれている…?

なるほどわからん。

Array.prototype.reduceの仕様

なるほどわからんなのは、私がArray.prototype.reduce関数を普段使っていないからのような気がします。reduceという関数に慣れていれば、上記の説明で一発理解ということになるのかもしれません。reduceは使ったこともあるし他の言語でもだいたいあるような一般的なものですが、ちゃんと仕様を読んで理解しているわけではありませんでした。

そこで、このreduce関数の仕様を見てみます。

さすがMDN、説明が丁寧です。これもじっくり読んでいきます。

reduce() はアキュムレータと配列の各要素に対して(左から右へ)関数を適用し、単一の値にします。

なるほどわからん。今度はまた新たななるほどわからんです。

今度のなるほどわからんは、「アキュムレータ」ってなんだ、という話です。こういうことを正直に書いてしまうとコンピュータのこと知らんやつだなというのが露呈するのであれなんですが、わからないものは仕方ない。

アキュムレータ(英: Accumulator)は、コンピュータにおいて、演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のことである。

accumulateというのが累積するという意味の英単語なので、そのままAccumulatorは累積するやつのようです。コンピュータアーキテクチャのことを学びたい気持ちも湧いてきてしまいますが、MDNのreduceのドキュメントを理解する上では、accumulatorという言葉の理解は上の説明で十分でしょう。

もう一度MDNのドキュメントに戻って、reduceが何をしているのかを見ていきます。

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

Reduceの使い方として、上のコードが例示されています。配列内の数の総和を出しているだけです。

やっていること、使い方はわかりますが、どう動作しているのでしょう。それについても書かれています。

なるほどわかりました。accumulatorがそれまでの返り値を保持していて、currentValueが配列内の現在の要素です。

let array = [1,2,3,4];
let accumulator = 0;
for (let i = 0; i < array.length(); i++) {
  let currentValue = array[i];
  accumulator += currentValue;
}

for文で書くとこんなかんじでしょうか。外で宣言した変数に詰めるのではなく、引数として渡ってくるのでreduceのほうがすっきり書けます。

Array.prototype.reduce?

Array.prototype.reduceがやっていることはわかったものの、

It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

これは結局どういうことでしょうか。ReduxのreducerがArray.prototype.reduce(reducer)として呼べるのであれば、reduceのレシーバに当たる配列は何になるんでしょうか?

Stack Overflowのある回答がとてもわかりやすかったです。

['INCREMENT', 'DECREMENT', 'INCREMENT'].reduce(reducer, 0)

つまりアクションの配列がレシーバになる。なるほど〜〜〜、そういうことを言っていたのか!0が最初のstate(accumulator)になり、reducerで定義された変更が3つ行われた結果が返ってくる。

雑感

Reducerというのが「stateを変えるためのあれ」くらいの認識だったけど、今回の調べで言葉の意味から理解できた。

ところで、reduceを調べているとfoldとか折り畳み関数みたいな言葉も出てきていた。次はここらへんについてもまとめてみたい。

参考リンクなど