LoginSignup
3
2

More than 3 years have passed since last update.

クロージャはlogを計算する関数を作ることで理解できる

Last updated at Posted at 2021-01-25
対数計算をする関数
>>> 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を受け取り、xmin未満ならminmax超なら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_minrerange内部にあります。②
①②より、今rerange内部にいます。
よって、引数maxminが有効です。

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]
>>>               
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2