ラムダについて

昨日に続いてラムダについて。
関数内でラムダとローカル変数について。

インスパイヤ先⇒お気楽 Python プログラミング入門:第3回再帰定義と高階関数

つまり、関数内で定義されたラムダ形式は、そのとき有効なローカル変数にアクセスすることができるのです。

とある。
ふむふむ。
ローカル変数を利用して、ラムダでごにょごにょとできるようだ。
関数内でラムダを利用することで、一発書き捨てのスクリプトから、引数を利用して多少の汎用性を持たせたものを作れるっちゅうことなのかな。

def times_element(n, ls):
    return map(lambda x: x*n, ls)

def remove_string(c, ls):
    return filter(lambda x: c != x[0], ls)

def main():
    numList = [1,2,3,4,5]
    print times_element(3, numList)

    numList = [3,3,45,3,5]
    print times_element(5, numList)

    strList = ["abc", "def", "ash", "you", "hello"]
    print remove_string("a", strList)

    strList = ["My", "name", "is", "nandakke?"]
    print remove_string("n", strList)

if __name__ == "__main__":
    main()

結果

D:\workspace\Python\3-2>python test.py
[3, 6, 9, 12, 15]
[15, 15, 225, 15, 25]
['def', 'you', 'hello']
['My', 'is']

引数の変更にも柔軟に対応しております、ってか。

追記

でもやっぱり

def times_element(n, ls):
    return [n*x for x in ls]

def remove_string(c, ls):
    return [x for x in ls if c != x[0]]

って書けるね。
うん。
ま、プログラミングってのはそういうものなのかもね。
でもlambdaちょっと面白いかも。
そして、lambdaがランバダって読めてしまうorz

関数のネスト

いわゆる入れ子の関数
C使いだった僕には新鮮な感じ。
そして、ようやく順列生成のエントリのid:morchinさんによるコードの全容が完全に理解できた。

def times_element(n, ls):
    def _timesN(x):
        return n*x
    return map(_timesN, ls)

def times_element2(n, ls):
    return map(lambda x: x*n, ls)

def main():
    numList = [1,2,3,4,5]
    print times_element(3, numList)
    print times_element2(3, numList)

if __name__ == "__main__":
    main()

結果

D:\workspace\Python\3-2>python test2.py
[3, 6, 9, 12, 15]
[3, 6, 9, 12, 15]

ってことで、さっきlambda使ってやったことと同じ処理が実現できる。
調子に乗ってアンダースコアをつけてみた。
さらに

関数を引数として渡す場合、簡単な処理ならばラムダ形式を使うことができますが、Python のラムダ形式は式を一つしか定義できません。複雑な処理を行いたい場合は、入れ子の関数を使うと便利です。

って部分から、そういうことですか〜。
if-else的な条件分岐なんかが入ってくるとまた事情が変わってくるみたいだな。
今度FizzBuzz問題でも書いてみませう。

クロージャ

クロージャは評価する関数と参照可能なローカル変数をまとめたものです。クロージャは関数のように実行することができますが、クロージャを生成するときに参照可能なローカル変数を保存するところが異なります。参照可能なローカル変数の集合を「環境」と呼ぶことがあります。


Pythonクロージャを生成するには「ラムダ形式」を使うか、局所的な関数を定義して、その関数を返します。

だって。
なんか面白い匂いがする。

def foo(n):
    return lambda x: n*x

foo10 = foo(10)
print foo10(10)

foo100 = foo(100)
print foo100(10)

結果

D:\workspace\Python\3-2>python test3.py
100
1000

むむ。
これは不思議で面白いぞ。
この場合だと、lambdaが関数、foo()に渡す引数が参照可能なローカル変数ってことか。
こりゃ面白い。

ちなみに、これを関数のネストを使って書くと。

def foo(n):
    def times(x):
        return n*x
    return times

foo10 = foo(10)
print foo10(10)

foo100 = foo(100)
print foo100(10)

関数をreturnするのが違和感たっぷり。
lambdaで書いたほうが直感的には分かりやすいかも。

カリー化

カリー化ってなんじゃい、って思ってよく考えると、ひところアルファーブロガーさん達のエントリでカリー化カリー化言うてたときがあったようなないような記憶が。
もちろんそのときはスルーしてましたが(笑)
今回はがんばってみよう。


……orz
よく分からんなぁ。
まず、wikipediaの説明が難しすぎるぜよ。
いろいろ調べた結果、恐らく
f(x,y,.....)=f(x)(y).....
って具合に、引数が多くあるときに、常に引数が一つの状態に持っていくことなのかと思いまふ。
それ以上はよくわかんね><


インスパイヤ先のサンプルコードだけではよく理解できなかったので引数を増やしてみる。
確かにそんな感じに仕上がっている。

def mapcar(func):
    def _mapcar(y):
        def _mapcar2(ls):
            return [x+y for x in ls]
        return _mapcar2
    return _mapcar

def main():
    numList = [1,2,3,4,5]
    y = 1

    f = mapcar(lambda x, y: x+y)
    f2 = f(y)
    print f2(numList)

    print mapcar(lambda x, y: x+y)(y)(numList)

if __name__ == "__main__":
    main()

結果

D:\workspace\Python\3-2>python test4.py
[2, 3, 4, 5, 6]
[2, 3, 4, 5, 6]

リストと、インクリメントする値が引数。
二ついっぺんに引数としてとって処理を行うのではなく、別々に引数としてとって処理する。
が、これで何がよろしいのかは不明。
Haskellとかの関数型言語では当たり前のことみたい。


そしてPythonにはfunctoolというカリー化のモジュールがあるらしい。