>>> import math
>>> def log(底): # エンクロージャ。関数「log底」を返す関数
... def log底(真数): # クロージャ。返される関数
... return math.log(真数, 底) # エンクロージャ内の変数「底」を、クロージャ内で利用する。
... return log底
...
>>> log(2)(8)
3.0
log(2)(8) == 3 の解説
1. log(2)の呼び出し
まずlog(2)が評価された時点で、エンクロージャlog(底)が呼び出される。
1-1. def log底(真数): return math.log(真数, 2)が実行される
エンクロージャ内で、クロージャの定義が実行される。この時、クロージャ内の変数はエンクロージャ内と共通であるため、
底==2が成立する。
1-2. return log底が実行される
エンクロージャ呼び出しlog(2)の評価値は、math.log(真数, 2)を返却する関数そのものである
2. log(2)(8)の呼び出し
1-2節より、log(2)は「math.log(真数, 2)を返却する関数」として評価されるのだった。
故に、log(2)は(lambda 真数: math.log(真数, 2))と同じようなことをを意味するのだ。
log(2)(8) つまり (lambda 真数: math.log(真数, 2))(8)が呼び出されることで、真数=8が代入され、
math.log(8, 2)が実行されるのである。
演習. xを受け取り、xがmin未満ならmin、max超ならmax、どちらでもないならxを返却する関数rerange(max,min)(x)を作れ
1. def rerange(max,min):
まずは外側の関数(エンクロージャ)を定義します。
1-1. def rerange_max_min(x):
関数rerange内に別の関数rerange_max_minを作ります。rerange_max_minが関数内関数、より具体的に言うとクロージャです。
1-1-1. y = max if x>max else (min if x<min else x)
今、rerange_max_min内部にいます。①
rerange_max_minはrerange内部にあります。②
①②より、今rerange内部にいます。
よって、引数maxとminが有効です。
1-1-2. return y
rerange_max_minの戻り値を宣言しました。
この瞬間、rerange_max_minは
xを引数として受け取り、max if x>max else (min if x<min else x)を返却する関数
となりました。
返却値max if x>max else (min if x<min else x)をよく見てみましょう。
変数は、max, x, minの3種類があります。
このうち、引数はxだけです。それ以外の2種類は、「引数ではない『謎』の変数」です。
『謎』解きは1-2節で行います
1-2. return rerange_max_min
rerangeの戻り値を宣言しました。
この瞬間、rerangeは
min,maxを引数として受け取り、
(
xを引数として受け取り、max if x>max else (min if x<min else x)を返却する関数
)
を返す関数となりました。
1-1節で「『謎』の変数」といったmin, maxは、rerange_max_minより外側の関数rerangeによって決まります。
故に、rerange_max_minだけに注目している限り「謎」の変数です。もっと視野を広げ、rerange全体まで見渡した今、謎が解けました。
>>> def rerange(max, min=0):
... def rerange_max_min(x):
... y = max if x>max else (min if x<min else x)
... return y
... return rerange_max_min
...
>>> [rerange(4,2)(成分) for 成分 in range(6)]
[2, 2, 2, 3, 4, 4]
>>> [rerange(4)(成分) for 成分 in range(6)]
[0, 1, 2, 3, 4, 4]
>>>