自前のマシンではSchemeで各種ツールやWebスクリプトを作ってきたが,昨今の安価・高機能・高性能なPaaS,SaaSでSchemeに対応しているはずもなく,代替言語としてのPythonで,Schemeの特徴のひとつである高階関数を扱うためのサンプルを作成・整理した.
なお,高階関数(Higher-order Function)とは,簡単に言えば『関数自体を値として引数にとったり戻り値としたりする関数』である.詳細は,『Structure and Interpretation of Computer Programs』"1.3 Formulating Abstractions with Higher-Order Procedures"を参照.
#高階関数の定義と利用
Pythonでもlambdaが使えるが単一の式としてしか扱えないため,defでローカル定義した関数を返す書き方が一般的らしい.
def threetimes(f):
def retfunc(x, y):
print(f(f(f(x, y), y), y))
return (retfunc)
def f(x, y):
return (2 * x + y)
threetimes(f)(10, 5)
# => f(f(f(x, y), y), y)
# => (2 * (2 * (2 * 10 + 5) + 5) + 5) => "115"
def threetimes_message(mes = ""):
def _threetimes(f):
def retfunc(x, y):
print(mes, end="")
print(f(f(f(x, y), y), y))
return (retfunc)
return (_threetimes)
threetimes_message("Result = ")(f)(10, 5)
# => "Result = 115"
threetimes_message()(f)(10, 5)
# => "115"
なお,対応するSchemeコードの例は次の通り.実行はGaucheで確認.
(define threetimes
(lambda (f)
(lambda (x y)
(print (f (f (f x y) y) y)))))
(define f (lambda (x y) (+ (* 2 x) y)))
((threetimes f) 10 5)
; => (f (f (f 10 5) 5) 5)
; => (+ (* 2 (+ (* 2 (+ (* 2 10) 5)) 5)) 5) => "115"
(define threetimes_message
(lambda (f . mes)
(lambda (x y)
(if (not (null? mes)) (display (car mes)))
(print (f (f (f x y) y) y)))))
((threetimes_message f "Result = ") 10 5)
; => "Result = 115"
((threetimes_message f) 10 5)
; => "115"
#デコレータ
高階関数を使用する際のsyntax sugar.語源は,デザインパターンの一種.Flaskなどのフレームワークを高階関数群として定義し,本来の処理を行うユーザ定義関数に機能追加するようにしたい場合に便利な書き方.
# threetimes,threetimes_message は higherorder.py の定義を使用
@threetimes
def f(x, y):
return(2 * x + y)
f(10, 5) # => "115"
@threetimes_message(mes = "Result = ")
def f(x, y):
return(2 * x + y)
f(10, 5) # => "Result = 115"
@threetimes_message()
def f(x, y):
return (2 * x + y)
f(10, 5) # => "115"
#lambda等を使ったその他サンプル
Wikipediaの記事(高階関数)にある例をいくつか抜粋.
def args_10_5(f):
def _args_10_5():
f(10, 5)
return (_args_10_5)
def f(x, y):
print("x = ", x, ", y = ", y)
args_10_5(f)() # => "x = 10 , y = 5"
def f(x, y, z, w):
return (4 * x + 3 * y + 2 * z + w)
f(2, 3, 4, 5) # => 30
def f(x):
return (lambda y: lambda z: lambda w: 4 * x + 3 * y + 2 * z + w)
f(2)(3)(4)(5) # => 30
def unfold(pred, f, update, seed):
if pred(seed):
return ([])
else:
r = unfold(pred, f, update, update(seed))
r.insert(0, f(seed))
return (r)
unfold(lambda x: x > 10, lambda x: x * x, lambda x: x + 1, 1)
# => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]