1. Python は一部の引数を与えて関数を呼び出すことができない
Haskell では、関数に複数の引数があるとき、先に一部の引数のみ渡しておき、後から残りの引数を渡すことができる。一部の引数を与えることを「部分適用」と言う。
Python では、普通そういうことはできない。例えば、3つの引数を足し合わせる関数 addThree 関数に対して、1つの引数だけ与える。
def addThree(a,b,c): return a + b + c print addThree(1)
上記を実行すると、引数が足りないとエラーが表示される。
exceptions.TypeError: addThree() takes exactly 3 arguments (1 given)
2. functools の partial 関数で部分適用
6.6 functools モジュールの partial 関数を使うと、部分適用を利用できる。
partial(func[,*args][, **keywords])
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords.
例えば、先ほど定義した addThree 関数に対して、partial 関数を使い、引数を1つだけ与える。
import functools addTwo = functools.partial(addThree,1) print addTwo(2,3)
partial 関数により、addThree 関数の最初の引数だけ渡した関数 addTwo を作成した。その後、addTwo 関数に対して、残りの二つの引数を与えている。
以下では、部分適用をした結果から、更に部分適用した addOneFromTwo 関数を作成し、元の addThree に二つ引数を渡した関数 addOne を定義した。
addOneFromTwo = functools.partial(addTwo,2) print addOneFromTwo(3) addOne = functools.partial(addThree,1,2) print addOne(3)
部分適用により、予め抽象的な関数を定義しておき、部分適用によって具体的な関数を導くことができる。上手く使えば、関数のモジュール化 を促進できる。
キーワード引数を利用して
キーワード引数を使うと、部分適用するときの引数を特定できる。
addTwo_ = functools.partial(addThree,c=3) print addTwo_(1,2) addOne_ = functools.partial(addTwo_,a=1) print addOne_(b=2) addOneFromTwo = functools.partial(addThree,a=1,c=3) print addOneFromTwo(b=2)
3. reduce 関数に対して部分適用し、具体的な関数を導く
リストの要素を足し合わせる sum 関数は、畳み込み関数 reduce より導くことができる。
なぜ関数プログラミングは重要か によると、
sum = reduce add 0
reduce 関数に対して、部分適用することによって sum を定義している。
Python の reduce 関数の使い方は、
print reduce(lambda a,b: a+b, [1,2,3,4,5])
reduce 関数に対して部分適用を利用し、シーケンスを後で与えるようにして、sum 関数を定義してみる。
L = [1,2,3,4,5] import operator from functools import partial # 合計 mysum = partial(reduce, operator.add) print mysum(L)
(functools.partial と書くのが面倒だったので、import の仕方を変更した。)
上記の書き方から、sum 関数が reduce というメタ的な関数の特殊パターンであるという雰囲気が伝わって来る。
- cf. Python で合計
Python では、(+) のように、二項演算子を渡すことができないので、3.10 operator モジュールの add を利用した。
4. 関数を合成する関数を定義
Haskell の 関数を合成する(.) 関数を、partial を使い Python で定義してみる。
- cf. Haskell の関数合成
関数を合成する関数名を `c’ とした。
c = lambda f,g: partial(lambda f,g,h: f(g(h)), f, g)
def … return で定義した方が読みやすいかな。
def c(f,g): return partial(lambda f,g,h: f(g(h)), f, g)
それとも、関数をネストして定義してみる。
def c(f,g): def _(f,g,h): return f(g(h)) return partial(_,f,g)
c 関数を使ってみる。
add3 = lambda x: x+3 mul3 = lambda x: x*3 print c(add3,mul3)(10) # 33 print c(mul3,add3)(10) # 39
関連記事
- Python でリスト内包表記を使ってクイックソート
- Python の map, filter, reduce とリスト内包表記
- Haskell の Prelude 散策 (1) - リスト操作 : 要素の重ね合わせ (folds)
- Haskell の部分適用 (2)
0コメント:
コメントを投稿