参考にした記事: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 )
)
すっきり。何となく意味もわかりやすいような。勉強になりました。