functools.reduce()

functools.reduce()

functools.reduce()

 functools.reduce() は「イテラブルの各要素に演算を連続的に作用させて1つの値に集約させる」高階関数 です。

 functools.reduce(function, iterable[, initializer])

 最初に functoolsモジュールから reduce()関数を、operatorモジュールから関数形式の標準演算子をインポートしておきます。

# リストA-1

# functoolsモジュールをインポート
from functools import reduce

# operatorモジュールから関数形式の標準演算子をインポート
from operator import add, sub, mul, truediv

 operator.add(), operator.sub(), operator.mul(), operator.truediv() は、それぞれ算術演算子 +, -, *, / に対応する関数形式の演算子です(関数の引数に足し算や引き算などの演算機能を渡したいときに使用します)。reduce()を用いた簡単な演算例を見てみましょう。

# リストA-2

# リストxを定義
x = [1, 2, 3, 4, 5]

# xの要素を順番に足し合わせる
y = reduce(add, x)

print(y)
15

 reduce(add, x) は x の各要素に順次 add()関数を作用させます。
 つまり、x の要素を合計しています。これは組み込み関数 sum() と同じなので、わざわざこのような面倒なことをする必要性を実感できませんが、reduce() を使うと「要素を順に引き算する」という演算も実行できます。

# リストA-3

# リストxを定義
x = [100, 10, 10, 10, 10]

# 最初の要素から2番目以降の要素を引き算する
# 100-10-10-10-10
y = reduce(sub, x)

print(y)
60

 同様に「要素を順に掛ける」「要素を順に割る」こともできます。

# リストA-4

# リストxを定義
x = [100, 2, 5, 2]

# y = 100×2×5×2
y = reduce(mul, x)

# z = 100÷2÷5÷2
z = reduce(truediv, x)

print("y = {}".format(y))
print("z = {}".format(z))
y = 2000
z = 5.0

 reduce() の第 3 引数 initializer で演算の初期値を指定することができます。

# リストA-5

# リストxを定義
x = [1, 2, 3, 4, 5]

# 初期値10にxの要素を順番に足し合わせる
# 10+1+2+3+4+5
y = reduce(add, x, 10)

print(y)
25

 関数形式の演算子の代わりに lambda式を使うこともできます。

# リストA-6

# リストxを定義
x = [1, 2, 3, 4, 5]

# xの要素を順番に足し合わせる
y = reduce(lambda a, b : a + b, x)

print(y)
15

 functools.reduce() の演算手順をもう少し詳しく見ておきましょう。
 第 1 引数 function は 2 つの引数を受け取ることのできる関数オブジェクトでなければなりません。たとえば、組み込み関数 pow(a, b) は a**b を返すので、この条件を満たしています。functools.reduce() の第 2 引数に x = [2, 2, 3, 3] というリストを渡したとすると、まず第 1 要素と第 2 要素を使って 2**2 を計算します。その次に (2**2) と 3 を pow() に渡して (2**2)**3 を計算し、最後に (2**2)**3 と 3 を pow() に渡して、((2**2)**3)**3 を計算します。つまり、2**2**3**3 を計算することになります。

# リストA-7

# リストxを定義
x = [2, 2, 3, 3]

# y=2**2**3**3
y = reduce(pow, x)

print(y)
262144

 このような処理を「畳み込み演算」とよぶこともあります。