Python でカリー化を実装する
カリー化は複数の引数を取る関数を、1 つの引数を取る関数のチェーンに変換する手法です。部分適用と組み合わせることで柔軟な関数合成が可能になります。
カリー化とは
# 通常の関数
def add(a, b, c):
return a + b + c
add(1, 2, 3) # 6
# カリー化された関数
def add_curried(a):
def f(b):
def g(c):
return a + b + c
return g
return f
add_curried(1)(2)(3) # 6
汎用的なカリー化関数
from functools import wraps
import inspect
def curry(func):
@wraps(func)
def curried(*args, **kwargs):
sig = inspect.signature(func)
try:
sig.bind(*args, **kwargs)
return func(*args, **kwargs)
except TypeError:
return lambda *more_args, **more_kwargs: curried(
*args, *more_args, **{**kwargs, **more_kwargs}
)
return curried
@curry
def add(a, b, c):
return a + b + c
print(add(1)(2)(3)) # 6
print(add(1, 2)(3)) # 6
print(add(1)(2, 3)) # 6
実用例:設定の部分適用
@curry
def format_message(template, name, value):
return template.format(name=name, value=value)
# テンプレートを固定
greeting = format_message("Hello, {name}! Your score is {value}.")
print(greeting("Alice")(100))
# Hello, Alice! Your score is 100.
部分適用との違い
カリー化
複数引数関数を 1 引数関数のチェーンに変換。f(a, b, c) → f(a)(b)©
部分適用
一部の引数を固定した新しい関数を作る。f(a, _, c) → g(b)
functools.partial との組み合わせ
from functools import partial
@curry
def power(base, exponent, modulo=None):
if modulo:
return pow(base, exponent, modulo)
return base ** exponent
square = power(exponent=2) # カリー化で base を待つ
print(square(5)) # 25
カリー化は関数型プログラミングの基盤であり、ポイントフリースタイルや関数合成と相性が良いです。