HOME | 備忘録 | 書き込む |
fcurried(x) は1つの引数をとる関数を返し、 その関数を呼び出すと、また1つの引数をとる関数 (fcurried(x)(y))を返し、 さらにその関数を呼び出す (fcurried(x)(y)(z)) と、もともとの関数 f(x,y,z) を呼び出したときの結果を返します。
また、Curry 化した関数は使いまわすことが可能です。
p = fcurried(x)(y) p(z1) --> f(x,y,z1) p(z2) --> f(x,y,z2)詳しくは Curry (Wikipedia) を見てください。
Curry 化についてはいろいろな人がいろいろな実装を試していますが、結構ごたごたしているので、 もっと簡単な方法はないか考えてみました。 その結果、以下に述べるような身も蓋もないやり方がシンプルに、かつ (コードは) エレガントに 実装できることに気づきました。
Curry 化された関数は、
[curry0.py]
001: #!python3.2 002: 003: from inspect import getfullargspec 004: from copy import copy 005: 006: 007: 008: def cons(ls, x): 009: ls1=copy(ls) 010: ls1.append(x) 011: return ls1 012: 013: 014: class Curry : 015: '''class of Curried function''' 016: 017: def __init__(self, fun, av=[]): 018: self.fun = fun 019: self.ac = len(getfullargspec(fun).args) 020: self.av=av 021: assert len(self.av) < self.ac 022: 023: 024: def __call__(self, val): 025: av = cons(self.av, val) 026: return self.fun(*av) if len(av) == self.ac else Curry(self.fun, av) 027:inspect モジュールの getfullargspec() を使うと関数がどのような引数をとるのか調べることができます。 通常の引数は argspec オブジェクトの args というリストに格納されているので、その長さを調べると引数の個数がわかります。 Curry 化された関数のオブジェクトは もともとの関数 (self.fun)、その関数がとる引数の数 (self.ac)、及び確定した引数のリスト (self.av) を保持しています。
curry0.py を import して試してみると以下のようになり、正常に動作しているのがわかります。
>>> from curry0 import Curry >>> def sub3(x,y,z): return x-y-z ... >>> csub3 = Curry(sub3) >>> csub3(10)(2)(3) 5 >>> p=csub3(10)(5) >>> p(1) 4 >>> p(3) 2また、次のようにデコレータとして用いることができます。
001: @Curry 002: def sub3(x,y,z): 003: return x-y-z
[curry.py] (抜粋)
001: def curry(fun, av=[]): 002: 003: ac = len(getfullargspec(fun).args) 004: assert len(av) < ac 005: 006: def curried(x): 007: av1=cons(av, x) 008: return fun(*av1) if len(av1) == ac else curry(fun, av1) 009: 010: return curriedcurry.py を import して試すと以下のようになり、正常に動作しているのがわかります。
>>> from curry import curry >>> def sub3(x,y,z): return x-y-z ... >>> csub3 = curry(sub3) >>> csub3(10)(2)(3) 5 >>> p=csub3(10)(5) >>> p(1) 4 >>> p(3) 2Curry クラスと同様にデコレータとして用いることができます。
001: @curry 002: def sub3(x,y,z): 003: return x-y-z
001: (define (curry fun . rest) 002: (let ((av (if (null? rest) '() (car rest))) 003: (ac (car (procedure-arity fun)))) ;; MIT-scheme only 004: (lambda (x) 005: (let ((av1 (cons x av))) 006: (if (= ac (length av1)) 007: (apply fun (reverse av1)) 008: (curry fun av1))))))Scheme の実装は Python の実装よりコードがごてごてした感じです。 Python は関数型言語としてもいけているといえます。
ソースはこちらからどうぞ。
HOME | 備忘録 | 書き込む |