python3x: lambda function
ラボをやっていたらlambda function
を見かけたので復習の意味も込めて。
Lambda Function
一言で言うとlambdaを使えば関数を簡単に適当な変数にアサインすることができる。
定義
bind a function to a name using the same syntax and assignment statement.
まずは以下のexpressionについて考えてみる。
>>> x = 10
>>> square = x*x
>>> square
100
>>> square = lamnda x: x*x
>>> square
<function...>
>>> square(4)
16
>> square(10)
100
講義スライドを参考に簡単に図にまとめてみた。
def
とlambda
の違い
上の例を参考に違いをまとめると:
- Both create a function with the same domain, range and behavior.
- Both functions have as their parent the frame in which they were defined.
- Both bind that function to the name square. (but in a different way: Lambda first creates the function with no name and it's the assignment statement that binds that function value to the name "square" whereas in the def statement, both of those things happen automatically, all as a byproduct of executing the def statement
- Only the def statement gives the function an intrinsic name.
基本的な動作に関してはそこまで大きい違いはない。では結局何に違いがあるのだというとそれはEnvironmental diagramを書いてみると分かる。以下は講義スライドをスクショしたもの。
def
で関数を作った場合その場で名前がふられるがlambda
で関数を作った場合はその関数が出来上がってもassignment statement
によって名前をアサインし終わるまでは名前を持っていないということ。つまりどういうことかというとpythonではわざわざdef statement
を使って名前付けしなくても必要なときにその場で関数を作り上げてしまうことができる。
A lambda expression evaluates to a function that has a single return expression as its body.
lambda expression
を実行した結果lambda function
となって関数としての役割を果たす。この関数には先天的な名前を持っていないためpythonはシンプルに<lambda>
として表記する。
>>> s = lambda x: x * x
>>> s
<function<lambda> at xxxxxxx>
>>> s(12)
144
それ以外で基本的な違いはない。
Translating between named and anonymous functions
ふと思ったことなのだが(そしてどこかで書いたかもしれないが)lambda x, y
とlambda x: lambda y
という二つのexpressionを比べた時にparameterの数とargument以外で違いが浮かばなかった。
>>> adder = lambda x: lambda y: x + y
>>> adder2 = lambda x, y: x + y
>>> adder(1)(3)
4
>>> adder2(1,3)
4
ただreview seshに参加した時にdef
とlambda
を比較することによってより違いがクリアになった。
考えてみたらこれは2個めのlambda
はdef
でいうdef helper(y): return
と考えたら分かりやすい。
Higher-Order Functionsもらくらくできてしまう。
def compose1(f,g):
return lambdax : f(g(x))
f = compose1(lamnda x: x * x,
lambda y: y + 1)
result = f(12) # 169
PythonTutor.comでより正確な関数の動きがわかる。
compose1
関数の中にあるlambda x:f(g(x))
関数はglobal frameではなくf1 frameで作られることに注意。理由はf
によってcompose1
が呼ばれて初めてcompose1
の中に入っていくからだ。compose1
を呼んだ時には既にf1 frameを作っているので[p=g]なわけだ。もう一つ気づいた点は気づいた点はcompose1
関数の引数として渡している2つのラムダについて。個人的にはcompose1
関数を呼んだ時に定義されるだろうと思っていたので[p=f1]と書いてしまったのだがどうやら上のcompose1(f,g)
と定義した時点で(何を受け取るか関係なく)変数に名前をアサインしているので後からlambda
として投げ込んでも何を投げ込んでも結果[p=g]なのだと思う。そんなラムダも使い方を間違えると余計にわかりにくくなる。
lambda expressions are notoriously illegible, despite their brevity.
どういうことかというと上の関数をcompose1 = lambda f,g: lambda x: f(g(x))
と書くこともできるが完全に理解するのに多少時間がかかることが多いと。
lambda
の起源
どうやらlambda
は当時のライターが数学の$ŷ. y * y$をという式をタイプライターで書けなかったことから生まれたとか。彼らはその表現を$Λy . y × y$と書き直した、というところから$λy . y × y$となり今でも$λ$という記号を観るようになったという。詳しくはPeter Norvigを。
lambdaを使って気づいたことを箇条書き
lab02の問題をやっていて気づいたことを書いていきます。
Question 1: WWPP: Lambda the Free
Q1
>>> c = lambda: 3
>>> c()
3
選択肢としては
1. None
をvalueとして出す (w/ some part missing?)
2. 3
を表示させるがNone
がvalue (=print)
3. 3
をvalueとして出す(=return)
くらいだろうか。ただ上の解説を見れば分かるようにlambdaにはreturn
が先天的に付属していると考えられているので当然def c(): return 3
と同じ。つまり3
を返す。
Q2
>>> c = lambda x: lambda: print('123')
>>> c(88)
<function <lambda>.<locals>.<lambda> at 0x1013740d0>
100%あたっているという自信は無いがpythontutor.comから推測するにc
はlambda x:
that returns lambda: print('123)
という構造になっていると読み取れる。恐らくそもそも二個目のlambdaは関数として呼ばれていないために最初のラムダが二個目のラムダを関数として引き取ってその関数をそのまま返したのだと思われる。これは後から気づいたことだが恐らくlambdaの中にlambdaを入れるという形を取ると最初のlambda関数が二個目のlambda関数を取ってとして返すという特徴があるのではないかと思ってきた。*ちなみにラムダは関数も受け取れる(というかその使い方のほうが頻度が高いのかもしれない)。
>>> d = lambda f: f(4) # They can have functions as arguments as well.
>>> def square(x):
... return x * x
>>> d(square)
16
つまり二個目も呼んであげればいいのだ。
>>> c = lambda x: lambda: print('123')
>>> c(88)()
123
しかし呼び方にも気をつける必要がある。なぜなら二個目のラムダには引数を受け取る機能がないからだ。def foo(): print("123")
と同じである。よって以下を試みると:
>>> c(88)(3333)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes 0 positional arguments but 1 was given
エラーを吐いてしまう。改善方法としては単純に引数とを与えてあげればいいのだ。
>>> c = lambda x: lambda y: print("123")
>>> c(88)(333)
123
>>> c(88)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'y'
しかしこうすると逆にargumentとして受け取るものがないとエラーを吐いてしまうので注意。
Q3
>>> t = lambda f: lambda x: f(f(f(x)))
>>> s = lambda x: x + 1
>>> t(s)(0)
3
詳しくはpythontutor.comを参考にして欲しいが、t(s)(0)
は(s)
と(0)
片方ずつs
から0
という順番で呼んでいくことと、lambda
の持っている変数がそれぞれ何を指しているのかということを注意してみると理解しやすいかもしれない。
ちなみにlambda内の変数はx
である必要はない。理由は受け取る際に自分が好みで指定した変数として受け取るからである。何もlambdaに限ったことではないが。
>>> t = lambda f: lambda y: f(f(f(y)))
>>> s = lambda x: x+1
>>> t(s)(0)
3
Q4
>>> bar = lambda y: lambda x: pow(x, y)
>>> bar()(15)
TypeError: <lambda>() missing 1 required positional argument: 'y'
これはlambda y
に対応するargumentが無いからである。bar(some #)(15)
で動く。
Q5
>>> foo = lambda: 32
>>> foobar = lambda x, y: x // y
>>> a = lambda x: foobar(foo(), bar(4)(x))
>>> a(2)
2
これはlambda
というよりHigher-Order functions向けに作られてると思うのでそこまで解説する必要はなさそう。
Q6
>>> b = lambda x, y: print('summer') # When is the body of this function run?
# Nothing gets printed by the interpreter
>>> c = b(4, 'dog')
summer
>>> print(c)
None
これはpure non-pure functionsを理解していればすぐ分かるはず。
Question 2: Question 2: Lambda the Environment Diagram
>>> a = lambda x: x * 2 + 1
>>> def b(b, x):
... return b(x + a(x))
>>> x = 3
>>> b(a, x)
上のコードをenvironmental diagramでかけるかという問題。
environmental diagramを書くときはframe名は必ずその関数の変数名ではなくその関数のintrinsic nameをつかってフレームを名付けることを忘れずに。
Question 3: Lambdas and Currying
Write a function lambda_curry2 that will curry any two argument function using lambdas. See the doctest if you're not sure what this means.
"""Returns a Curried version of a two argument function func.
>>> from operator import add
>>> x = lambda_curry2(add)
>>> y = x(3)
>>> y(5)
8
"""
"*** YOUR CODE HERE ***"
return
どの関数がどのvalueを受け取っているのかに注意しながら考えると簡単に解ける問題。例えばlambda_curry2
関数は関数を引数として受け取っているということがx = lambda_curry2(add)
よりわかる。次に変数y
に数字を1つずつ入れて8
が返ってきている。ということはlambdaは数字を2を受け取ってあとの処理はlambda_curry2
が取ってきた関数を使えばいいでしょという結論にたどり着く。よって答えはreturn lambda x: lambda y: func(x, y)
でオッケー。疑問はなぜreturn lambda x, z: func(x, z)
ではダメなのかということだったのだが、それはλを呼ぶときにy = x(3)(5)
とするかy = (3,5)
とするかの違いだけなのではないかと思っている。
>>> def lambda_curry(func):
... return lambda x, y: func(x,y)
>>> from operator import add
>>> x = lambda_curry(add)
>>> y = x(3)(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> l = x(3,5)
>>> l
8
残りの問題は一応解き終わっているのだが、higher-order functionsが関わってくるのでそこをもうちょっと深く勉強して人に説明できるレベルにまで達したら更新することにします。
Lambda w/ List Comprehension
lambda
とlist comprehensionを使って中々実用的なことができることを発見したので備忘録がてら。
あまり効率的な探し方ではないが素数を探すときにlambda
を使って以下のように書くことができる。
nums = range(2,30)
for i in range(2,8):
nums = list(filter(lambda x: x == i or x % i, nums))
print(nums)
もう一つは適当な文章を見付けて来てそれをsentenceとしていれてあとは以下のスクリプトを動かせば単語ごとの文字数をリストにいれて返してくれる。例えば:
sentence = "I was a joke, and my life was a joke."
print(list(map(lambda x: len(x), sentence.split(" ")))) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
print(len(list(map(lambda x: len(x), sentence.split(" "))))) # 10 (=# of words)
参考にしたリンク
Author And Source
この問題について(python3x: lambda function), 我々は、より多くの情報をここで見つけました https://qiita.com/weedslayer/items/18fc3b2861681d37db5a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .