今回は関数型プログラミングの中で頻繁に登場するカリー化というテクニックを紹介します。
カリー化はpartial関数のシンタックスシュガーであるためpartial関数について知っていれば難しくはありません。partialの復習も兼ねてカリー化と比較しながら紹介します。
partial関数のおさらい
partial関数は関数に部分適用できる機能を持っています。
例えば、引数を2つ受け取りその積を返す関数mul(a, b)があります。どんな数値も2倍にする関数doubled(b)が欲しい場合は、partial関数を使って以下のように作成できます。
doubled = partial(mul, 2) # 部分適用
print(doubled(2))
print(doubled(11))
【結果】
4
22
カリー化
カリー化は部分適用のシンタックスシュガーです。つまりpartial関数を簡潔に表現できるものです。ちなみにカリー化の”カリー”とは人名に由来するものです。
複数の高次関数をチェーン化する時に役立ちます。
カリー化された関数は、結果を出力するために必要な引数を得られなかった場合、部分適用された関数を返します。
以下のようにcurryデコレータを使って関数を定義するだけでカリー化できます。
from toolz import curry
@curry
def mul(x, y):
return x * y
curryクラスの初期化引数に渡してカリー化することも可能です。
mul = curry(mul)
カリー化した関数を以下のように使うことができます。
doubled = mul(2)
print(doubled(5))
print(mul(3)(2))
【結果】
10
6
mul関数は引数が2つ必要ですが、1つしか受け取っていません。そのため、xに2を部分適用した関数を返します。doubleは以下の関数と同等です。
def double(y):
return 2 * y
カリー化されたmul関数は以下と同等の関数になります。
def mul(x):
def mul_x(y):
return x * y
return mul_x
引数を1つ取る関数の入れ子構造になっていることがわかります。
上記の”mul(3)(2)”のようにmul関数の後に”(2)”をつけることでmul関数内のmul_x関数の引数に 2 を渡すことができます。
mapをカリー化
以下のようなリストの要素を全て2倍にしたリストを生成する処理があるとします。
def doubled(a):
return a*2
nums = [1,2,3,4,5]
doubled = partial(map, doubled) # (1)部分適用
r = list(doubled(nums)) # r = [2, 4, 6, 8, 10]
上記コードの”(1)部分適用”とコメントしてある行は、次のようにmapをカリー化し、partialで部分適用していた箇所を、カリー化したmap関数で置き換えられます。
map = curry(map)
doubled = map(doubled)
このように部分適用可能なmapにすることで簡潔に書くことができます。
また、上記の例からカリー化はpartial関数のシンタックスシュガーであることが伝わると思います。
このケースに限ってはpartialの方が1行で書けるのでシンプルです。
ただし、partial関数で何度も部分適用するような関数がある場合は、カリー化した関数や、curried名前空間に切り替えた方が便利な時があります。
Curried名前空間
toolz名前空間にあるすべての関数は、toolz.curried名前空間でカリー化されています。そのため、インポート文を
from toolz import *
から、
from toolz.curried import *
のようにすることで、カリー化されたtoolzの関数を使うことができます。
map, filter, reduceのようなPython標準の高階関数をカリー化したものもあります。
具体的にどのような関数が利用できるかは、githubのページを参照してください。
参考サイト:PyToolz API Documentation
コメントを残す