プログラマーはなぜ「reduce」を嫌うのか

Anecdotally, programmers dislike "reduce"

mapやfilterはコードレビューでほとんど文句を言われないのに、reduceを使うと「読みにくい」と指摘されることが多い。筆者はこの経験から、プログラマーがreduceを好まない傾向があると感じ、その理由として可読性の低さ、馴染みの薄さ、パフォーマンス問題、言語によるエレガンスの違いなどを挙げている。Clojureではこの反応がなかったという。

短く言えば、私の経験では、人はmapとfilterは好むが、reduceは好まない。
  1. rspeele

    mapとfilterは普通引数が1つで、2つある場合の2つ目はほぼ必ず0始まりのインデックスだ。ほとんどの言語で見た目は同じで、MicrosoftがSelectとWhereと呼ぶことにしても変わらない。

    reduceにはアキュムレータと2引数の関数があって、言語間でreduce(initial_acc, callback(acc, elem))なのかreduce(callback(acc, elem), initial_acc)なのかreduce(callback(elem, acc), initial_acc)なのか、それとも何か別なのか、全然一貫していない。

    覚えにくい。さらに、初期アキュムレータを全く取らないreduceのバージョンを持つ言語もあって、これは空のコレクションにぶち当たるのを待っているだけの足を撃ち抜く銃だ。しかも、匿名型をサポートしていない、あるいは匿名型レコードの簡単な変更をサポートしていない言語では、アキュムレータはすぐに扱いにくくなる。ほとんどの言語がそうだ!

  2. chubot

    パフォーマンスが悪いという点に関連して、reduceがPython 3から「追放」された——Python 2の組み込みreduce()ではなくfunctools.reduce()に格下げされた——とき、私は間違いなくその場にいたと思う。

    話は、2006年か2007年ごろ、Guido van RossumがGoogleの社内コードレビューツール(彼が書いた)のあるWebページがレンダリングに30秒以上かかる理由をデバッグしていたときにさかのぼる。

    これは基本的に「本番」インシデントで、何千人ものGoogleのエンジニアがそのツールに依存していた。このようなリクエストがスレッドを塞ぎ、スレッドプールを枯渇させていたのだろう。

    最終的に、reduce()で書かれた行折り返しアルゴリズムに行き着いた。彼が書いたものではないと思う——依存関係を通じて入ってきたのかもしれない。多くの人が知っているように、reduce()は基本的にこうだ:

    s1 + s2

    s1 + s2 + s3

    s1 + s2 + s3 + s4

    ...

    そしてs_iが文字列の場合、これはO(n^2)になる。5000行以上のdiffや5000行以上のファイルを見るとこれが現れたのだと思う。(Githubのような新しいプログラムもここで苦しんでいる)

    当時のPythonでは、+=はすでにこれを避けるように最適化されていたはずだ(本質的にすべてのJS VMがそうであるように)。あるいは、listにappend()してからjoin()するイディオムを使うこともできる。

    しかしreduce()は基本的に非効率な実装を強制する。そしてこれはPython 3でもまだ真実だと確信している。

    ---

    つまり、Guidoはreduce()に関連するパフォーマンス問題のデバッグに長い時間を費やし、ユーザーが「足を撃ち抜く銃」を避けられるように、それを追い出す決定を下した。当時私は彼のオフィスメイトだったのでこれを覚えているが、私はその場にいなかった[…]

  3. catapart

    私にとっては名前[0]だ。mapは別の配列からマップされた配列を出力する。filterは入力配列をフィルタリングした配列を出力する。どちらも常に真だ。一方reduceは、入力配列の縮約(おそらくほとんどの場合)を出力するかもしれないが、そうでないかもしれないという事実は、起こっていることが実際には縮約ではないことを意味する。js/tsのような言語では、入力配列の要素と同じ型のものを返す必要すらない。整数の配列を文字通りキャンセルトークンや状態オブジェクト、その他何にでも「reduce」できる。

    これが最も効率的な作業方法でないことはわかっているが、私はコードが指示のように読めるのが好きだ。reduceにできることでforループにできないことは何もなく、forループ(+もちろんアキュムレータ)の方がreduceよりも明確に「読める」。mapを読めば何が起きているかわかる。filterを読めば何が起きているかわかる。reduceを読むと、たとえ何が起きているか大体わかっていても、何が起きているかを解読しなければならない。reduceが常に入力配列の要素を返すと信頼できればもっと使うだろう。しかし何でも返せるので、私はforループのシンプルさを好む。

    [0] 「より良い」名前の提案はない。操作全体を一言で要約するのは難しいから?「dispatch」は、配列の各要素に関数をディスパッチする関数として意味が通るが、戻り値からの蓄積という概念を覆い隠してしまう。「transform」は正確だが、説明としてはほとんど[…]

  4. kevinwang

    Pythonで:リスト関数(map、filter、reduce?)のうち、削除されたのはreduceなのに、私が時々手を伸ばす唯一のものであるのはいつも面白いと思う。(存在しないことを思い出すと、たいていはより読みやすい3行のforループを書いて満足する。)

    他の2つはリスト内包表記で簡単に表現できるが、reduceではそれはできないと思う(できたとしてもおそらくひどいものになる)。

  5. snackbroken

    mapとfilterは、単一の要素を孤立してローカルに推論できるので良い。reduce(fold)は中間結果についてグローバルに推論することを強制する。reduceはまた、関連する型の「ゼロ」値を頭の中に呼び出すことを強制する。これは通常難しくはないが、余分な精神的オーバーヘッドになる。

  6. el_oni

    仕事でreduceを使ったのは片手で数えるほどしかないが、そのたびに眉をひそめられる。

    しかし、たくさんのSparkデータフレームをunionするなら、

    df = reduce(DataFrame.union, list_of_dfs)

    は

    df, *rest = list_of_dfs

    for other in rest:

    df = df.union(other)

    よりもずっと良いと思う。

    人々はちょっと変な反応をする。特に今はfunctoolsからインポートしなければならないからね。

  7. antfarm

    2008年に最初のRailsの仕事でRubyを学んだとき、ようやくクロージャとreduceがわかったのを覚えている。

    1995年に最初の学期のCSの授業(Modula 2で教えられた)で再帰とポインタがようやく理解できたときと同じレベルの決定的な瞬間だった。どちらもプログラミングの本で読んだだけで、自分では理解できなかった概念だった。

    2024年にはSwiftでAdvent of Codeを、可変状態、カスタムデータ型、ループを使わずにやり、reduceをかなり惜しみなく使った。[1]

    [1] https://github.com/search?q=repo%3Aantfarm%2FAdventOfCode202...

  8. s-zeng

    関数型プログラミングの世界でさえ、`fold`はちょっとしたコードの臭いだという議論がある。命令型コードベースにおける`while`が少し臭うのと同じようなものだ。それぞれに使うべき正当な理由はあるが、それらはあまりに低レベルな反復プリミティブなので、より高レベルなもの(命令型プログラムではforループやイテレータ、FPではモノイド的なreduce(アキュムレータがリスト要素と異なる型であるfoldとは対照的に)、モナディックtraverse、再帰スキームなど)を使った方が良いかもしれない。whileループでイテレータやforループを実装できるとしても、おそらくすべきではない。関数型の走査についても同様だ。

    しかしPythonやJavaのような言語では、より強力な関数型の走査の多くに実際にはアクセスできない。だからwhileループしかない言語で作業するのと同じような窮地に立たされる。

この日のほかの記事

2026-09-16