2013/12/29

Pythonの基本的な高階関数

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 について順に見ていきます。

map

map はリストやタプルの各要素に指定した関数を適用し、各適用結果を順番に返すイテレータを返す関数です。 map(func, iterator) の形でつかいます。
a = [-1, 3, -5, 7, -9]

print list(map(abs, a))
# => [1, 3, 5, 7, 9]
これは各要素の絶対値を計算する処理をしています。

ここで、absは引数をひとつ取り、その絶対値を返す関数。listはイテレータをひとつ取り、リストとして返す関数です。

listやprintはここでは表示用に使っているだけです。

map の結果として a の各要素の絶対値が計算されて新たなリストが返ってきています。

ちなみに、この同じ処理をジェネレータ式で書くと次のようになります。
(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 は演算子の「+」と同じ処理を行うための関数です。「+の関数版」と捉えるとよろしいかと思います。演算子である + を reduce に直接渡すことはできないため、かわりに add を使っています。

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

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

ちなみに、タプル内包表記はあくまでもイテレータを返すものなので、畳み込み演算に対応した表記はありません。

おまけ: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 件のコメント: