【Python】reduce関数の使い方の基本 ~要素の畳み込み~

Python で reduce 関数を使って要素を畳み込む方法を解説します。
reduce関数で要素を畳み込む
reduce 関数は、関数と処理対象のイテラブルを受け取り、そのイテラブルの要素を指定した関数を使って畳み込むための関数です。
このように引数に関数を受け取る関数は高階関数と言います。for 文で処理しても同じ処理ができますが、繰り返し処理を書かずにシンプルに記載できる点がメリットです。
reduce 関数は、Python 2 では map 関数や filter 関数同様に Python の組み込み関数でした。しかし、Python 3 からは functools モジュールに移動したため、この関数を使用する際には functools のインポートが必要となりました。
reduce 関数は関数型プログラミング言語でも中心的な関数です。一部の言語では fold という関数名が使われていることもあります。Python はマルチパラダイムの言語と言われているため、厳密ではありませんが関数型のプログラミングスタイルもサポートしており、reduce 関数が利用できます。
本記事では、リストを使った例を用いて、reduce 関数の使い方の基本を紹介します。
reduce 関数の基本的な使い方
reduce 関数の基本的な構文は以下のようになります。
from functools import reduce result = reduce(関数, イテラブル)
reduce 関数は functools モジュール内にあるためインポートが必要です。
reduce 関数は、イテラブルの要素を 1 つずつ取得し、それらの要素に累積的に指定した関数を適用して結果を畳み込んでいきます。例えば、関数 func とリスト [a, b, c, d] というものがあった時に reduce(func, [a, b, c, d]) は、func(func(func(a, b), c), d) と同じです。
よく reduce 関数と一緒に紹介される map 関数や filter 関数は返却値としてイテレータを返すため無限に続くシーケンスでも動作させることができます。もちろん list 等に変換しようとしたり、for でループするようなことをすれば終了しなくなります。
一方で、reduce 関数は計算のためにイテラブル内の全ての要素を評価しないといけないため、無限のシーケンスでは利用することはできませんので注意しましょう。
基本的な使い方
reduce 関数の基本的な使い方を以下の例を用いて見ていきましょう。
from functools import reduce
def add_nums(x, y):
"""値を足し算する"""
return x + y
def multiply_nums(x, y):
"""値を掛け算する"""
return x * y
if __name__ == "__main__":
tmp_list = [1, 2, 3, 4, 5]
# reduce関数でadd_nums関数を適用する
result = reduce(add_nums, tmp_list)
print(result)
# reduce関数でmultiply_nums関数を適用する
result = reduce(multiply_nums, tmp_list)
print(result)【実行結果】 15 120
上記の例では、受け取った値を足し算する add_nums 関数と掛け算をする multiply_nums 関数を用意し、tmp_list の各要素に順に関数を適用しています。
reduce 関数を呼び出している部分では「reduce(add_nums, tmp_list)」というように関数と処理対象のリストを渡しています。ここでポイントは、関数を渡す際に () がないことです。関数は add_nums(1, 2) のように () で引数を指定すると関数を実行することを意味しますが、() がない場合は関数そのものを表します。reduce 関数は、受け取った add_nums や multiply_nums といった関数を内部で実行しているわけです。
reduce 関数で add_nums と tmp_list を実行した場合、具体的に計算している内容としては「((((1+2)+3)+4)+5) = 15」です。同様に multiply_nums の場合は、「((((1*2)*3)*4)*5) = 120」となります。
このような計算を畳み込みといい reduce 関数を用いることで、リストの要素に順に関数を適用して計算することができます。
ラムダ関数(無名関数)と組み合わせる場合
reduce 関数は、ラムダ関数(無名関数)と組み合わせることで、より手軽に畳み込み計算をすることができます。ラムダ関数は、関数定義を def で定義することなく、その場で作れるもので関数型プログラミングでは中核の概念です。
add_nums 関数や multiply_nums 関数をラムダ関数にして使う例を見てみましょう。
from functools import reduce tmp_list = [1, 2, 3, 4, 5] # reduce関数でラムダ関数を使って足し算を適用する result = reduce(lambda x, y: x + y, tmp_list) print(result) # reduce関数でラムダ関数を使って掛け算を適用する result = reduce(lambda x, y: x * y, tmp_list) print(result)
【実行結果】 15 120
これまでの例で add_nums 関数を指定した部分で「lambda x, y: x + y」というラムダ関数で置き換えて指定しています。また、同様に multiply_nums の代わりに「lambda x, y: x * y」としています。結果は add_nums 関数や multiply_nums 関数を使った場合と同じです。
ラムダ関数は無名関数ともいわれる通り、関数名はありませんが、そのものが関数として扱われるため、reduce 関数に渡すことでリスト要素に対して畳み込みができます。
まとめ
Python で reduce 関数を使って要素を畳み込む方法を解説しました。
reduce 関数は、関数と処理対象のイテラブルを受け取り、そのイテラブルの要素を指定した関数を使って畳み込むための関数です。reduce 関数は関数型プログラミング言語でも中心的な関数ですが Python でも使用できます。
reduce 関数は、Python 3 からは functools モジュールに移動したため、この関数を使用する際には functools のインポートが必要になります。
reduce 関数は for 文を用いなくてもシンプルに関数の畳み込みができることが特徴です。非常に強力な機能ではありますが、reduce 関数の適用が常に最適な選択というわけではありません。
場合によっては for 文で書いた方が可読性が上がる可能性もあります。目的に応じて適切に方法を選択できるように reduce 関数の使い方を理解しておくようにしましょう。
上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。


