pythonのデコレータについて
結構面白い。
def dec(fn): return lambda x, *arg, **kw : fn(x-1,*arg,**kw) @dec def f(n): print "input: %d" % n return 1 if n<=0 else n * f(n-1) print f(10) # input: 9 # input: 7 # input: 5 # input: 3 # input: 1 # input: -1 # 945
これは結局、関数を受け取って関数を返す関数を書けば良いだけなので
(*args -> **kw -> a) -> (*arg -> **kw -> a)
schemeで書くと以下のような感じ。
(define (dec$ fn) (lambda (x . args) (apply fn (- x 1) args))) (define (f n) (if (<= n 1) 1 (* n (f (- n 1))))) ((dec$ f) 10) ; => 362880
嘘。これではうまくいかない。内部でdec$によって変換されたfが使える必要がある。
(define (dec$ fn) (lambda (x . args) (apply fn (- x 1) args))) (define (f n) (print "input:" n) (if (<= n 1) 1 (* n (f (- n 1))))) (update! f dec$) ;; (set! f (dec$ f)) (f 10) ;; input:9 ;; input:7 ;; input:5 ;; input:3 ;; input:1 ;; 945
同じ挙動になった。ただこれはデコレータがない時のpythonの書き方と同じ感じ。デコレータをつくらないといけない。デコレータはひとつ下の関数にしか適用されないのだからこんな感じに。名前を取るようにするととても楽。
(define-syntax with-decorator (syntax-rules () [(_ decorator name definition) (begin definition (update! name decorator) name)])) (with-decorator dec$ g (define (g n) (print "input:" n) (if (<= n 1) 1 (* n (g (- n 1)))))) (g 10) ;; input:9 ;; input:7 ;; input:5 ;; input:3 ;; input:1 ;; 945
デコレータは引数をとる形でもつかえた
たぶん以下のようになっているだけ。
*args -> (*args -> **kw -> a) -> (*args -> **kw -> a)
きっとこういう風に定義すればうまく動作する。
def natural(default): def _natural(fn): def __natural(x, *args, **kw): if x < 0: return default else: return fn(x, *args) return __natural return _natural @natural(None) def f(n): def g(n,acc): return acc if n <= 1 else g(n-1,acc*n) return g(n,1) print f(10) # => 3628800 print f(-100) # => None
うまくいった。
実際のところ
with-decoratorとschemeのsyntaxはあまり合わなそう。でもメモ化などが手軽に定義できるので便利な機能ではある。