2013/12/29

Python の基本的な高階関数( map() filter() reduce() )

Python の高階関数のうち、組み込みで用意されている基本的なものについてご紹介します。

具体的には
  • map()
  • filter()
  • reduce()
の3つを見ていきます。

個別の説明に入る前に、まずは「高階関数」について少し説明を。

高階関数とは、英語の「 higher-order function 」の訳で、「他の関数を引数として受け取る関数」のことです。たとえば、リストの各要素に共通の処理を加えたリストを作成したい場合なんかに、処理をわかりやすく簡潔に記述することができます。

高階関数の仕組みを用意している言語は他にもたくさんありますが、 Python の場合は「関数はオブジェクトであり、関数に () をつけると初めて関数が呼び出される」決まりとなっています。 () なしで関数の名前だけを指定すればその関数そのものを捉えることができます。

たとえば sin 関数の場合。

print sin(10)  # => sin(10)の計算結果

print sin  # => sin関数を表すオブジェクト

my_sin = sin
print my_sin(10)  # => sin(10)の計算結果

というようなことになります。

では、 map() filter() reduce() の 3 つについて順に見ていきましょう。

map()


map() はリストやタプルの各要素に指定した関数を適用し、各適用結果を順番に返すイテレータを返す関数です。 map(func, iterator) の形で使います。

a = [-1, 3, -5, 7, -9]

print list(map(abs, a))
# => [1, 3, 5, 7, 9]

これは各要素の絶対値を計算する処理をしています。

ここで、 abs() は引数をひとつ取り、その絶対値を返す関数です。 list はイテレータを受け取ってリストとして返す関数(リストのコンストラクタ)です。 list() については、ここでは中身を表示するために使っているだけなので map() の使い方に直接は関係ありません。

map() の戻り値は、リスト a の各要素が abs() に渡された結果の新たなリストとなります( Python3 の場合はイテレータとなります)。

ちなみに、同様の処理をジェネレータ式で書くと次のようになります。

(abs(x) for x in a)  # => map(abs, a) と同じ結果

filter()

つづいて filter() です。 filter() はリストやタプルの要素のうち関数を適用した結果が True となるものだけをイテレータとして返す関数です。
a = [-1, 3, -5, 7, -9]

print list(filter(lambda x: abs(x) > 5, a))
# => [7, -9]
ここでは、無名関数 lambda を使いました。ここで lambda は「絶対値が 5 よりも大きいなら True 」を返す関数です。

filter() の結果として a の要素のうち絶対値が 5 よりも大きいものだけがリストで返ってきています。

ちなみに、こちらもタプル内包表記で書くと次のようになります。
(x for x in a if abs(x) > 5)  # => 上記filterと同じ結果

reduce()

最後に reduce() を見てみます。 reduce() はリストやタプルの要素を足しあわせたりかけあわせたりする関数です。「畳み込み」演算と呼ばれたりします。英語でいうと「 folding 」「 convolution 」でしょうか。

from operator import add
# from functools import reduce  # python 3 では必要

a = [-1, 3, -5, 7, -9]

print reduce(add, a)
# => -5

これはすべての要素を足し合わせるサメーションの計算をしています。

ここで、関数 add は演算子の「 + 」と同じ処理を行う関数です。 Python では演算子である + を reduce に渡すようなことはできないため、 + のかわりに add を使っています。

reduce() の結果として a の要素の総和である -5 が返ってきています。

もちろん、 lambda を使っても同様の処理を行うことが可能です。
print reduce(lambda x, y: x + y, a)

ちなみに、畳み込み演算に対応した内包表記はありません。

追記: Python 3 では reduce() は標準ライブラリ functools に移動されたので、利用するには import してくる必要があります。


おまけ: apply()

Python の 2.3 あたりまでは apply() という関数もあったそうです。しかし、 apply でできる処理は「 * 演算子」(リストなどを展開するスプラット演算子)を使った引数展開によって可能なので(可能となったので?)、推奨されていないようです。

ちなみに、 * 演算子での引数展開は次のように行います。

from operator import add  # addは引数を2つ取る関数

a = [1, 2]

print add(*a)  # => 3
# add は引数を2つ取るインタフェースとなっているため
# リストaを直接受け取ることはできない
# add(*a) とすると a の中身が展開され
# add(1, 2) と同じ動きをする

以上です。


余談ですが、四則演算のための演算子( + - * / )や比較演算子( < <= == >= > )を高階関数に渡したいときは、 operator ライブラリを使うと便利です。演算子と同じ働きをする関数がまとめられています。


参考
高階関数について
高階関数とは - はてなキーワード
高階関数 - Wikipedia
たのしい高階関数

Pythonの高階関数について
2. Built-in Functions — Python公式ドキュメント
Python の map, filter, reduce とリスト内包表記 | すぐに忘れる脳みそのためのメモ
Understanding the Map function. python - Stack Overflow
list - Filters in Python - Stack Overflow

operatorライブラリについて
9.9. operator — Standard operators as functions — Python v2.7.6 documentation

0 件のコメント: