Python
ラムダ式

Python : lambdaの中でも再帰できた

参考にした記事:Fizz Buzz問題で副作用と代入を抑えてみる【JavaScript、VB.Net、Python3、CommonLisp、Clojure、HSP、R言語】のコメント
Pythonでのワンライナー:再帰
Yコンビネータなんて複雑なものを作らなくても、関数に自分自身を受け取る引数があればいいのです。

lambdaの中に再帰は書けない(特別なことをしない限り)と思ってたんですけど、できるんですね。知らんかった。
いや、以前は読んでも理解できなくて脳内スルーしてたんだと思う。

記事を参考に自分でも階乗を書いてみました。

do = pack_to_tuple = lambda *x : x
case = unpack_and_evaluate_in_order_then_return_last = lambda x : x[-1]
otherwise = True

f = ( lambda x :
        case(     x == 0   and do( 1 )
              or otherwise and do( x * f( x - 1 ) )
             )
    )

f2 = ( lambda x :
       ( lambda f : f( f,  x ) ) ( lambda f, x :
                                     case(     x == 0   and do( 1 )
                                           or otherwise and do( x * f(f,  x - 1 ) )
                                         )
                                 )
     )

print(f(10), f2(10))
# 結果
3628800 3628800

※ 最初の三行は、Haskellっぽくガードっぽい条件分岐をするためのものです。
できた!

上の関数fと下の関数f2と同じ事をしています。
上の方は普通に再帰してます。こっちは普段やってるし理解できる。
下の方は?ちょっと難しい...
無名再帰で階乗を返す関数を作って、f2という名前をつけている、って感じでしょうか?

  • x と f を引数とし f(f,x) を返り値とする無名関数の引数 f に、
  • f と x を引数とし、x の値によって 1 または x*f(f,x-1) を返す無名関数を渡す
  • x を引数とする無名関数

何となくわかるようなわからないような...でも、上から下に機械的に変換することはできるので、必要になったときにはなんとかなりそうです。

違うよ、とか、こういうことだよ、とかあればコメントお願いします。

追記 これでいいのか

参考記事:【Python】lambdaで再帰

キーワード引数を使うと...

f3 = ( lambda x, f =  lambda f, x : case(     x == 0   and do( 1 )
                                        or otherwise and do( x * f(f,  x - 1 ) )
                                    )
       : f( f,  x ) 
     )

すっきり。何となく意味もわかりやすいような。勉強になりました。