読者です 読者をやめる 読者になる 読者になる

無名関数を自己参照したい

python

多分こんなこと誰だって思うことだろうから, 他にいい方法があると思うが, パッと出来た方法をメモしておきます.

例によって階乗の計算を例に挙げます.

JavaScriptならこんな感じですか.

(function(n){return n > 1 ? n * arguments.callee(n-1) : 1})(20)

arguments.calleeってのが自分参照しちゃってるよ...



これPythonでも出きるんじゃね?

と思って調べるも, なかなか出てこない.

仕方がないのでこんな感じで書いてみた.

def mylambda(s):
  def g(x):
    this = g
    return eval(s)
  return g
print mylambda('x*2')(20)   # same as print (lambda x:x*2)(20)
print mylambda('x * this (x - 1) if x > 1 else 1')(20)   # same as ....?

mylambda('x*2')(20)は(lambda x:x*2)(20)と同じ事.

関数mylambda('x * this (x - 1) if x > 1 else 1')のthisが自己参照のつもり

(lambda x:x * this (x - 1) if x > 1 else 1)って書けたらいいのになぁという思いを込めて.

たぶんこれが無理なのはどこまで広く関数を参照すればいいのか曖昧だからだろうね.

引数にxしか使えないし, 引数の数も一つだけ...これはタプル渡せばなんとかなるけど.

二項係数を計算したいときはこんな感じ:

print mylambda('1 if x[0] == x[1] or x[1] == 0 else this((x[0] - 1, x[1] - 1)) + this((x[0] - 1, x[1]))')((10, 3))

便利そう...

evalケマイケマイ...こんなのだれも使わないようにwww



次のコードでもいけるみたい.

mylambda=lambda x:eval('lambda x:'+x,{'this':lambda y:mylambda(x)(y)})




で, 今回のオチはJ

Jで階乗を計算するには

!20

でおk. これだと2.4329e18ってなっちゃうから,

x:!20

とすれば, ちゃんと2432902008176640000って出る.

これじゃ元も子もないじゃん.



階乗の式を自分で定義しようと思ったら, まずは次の式を書く.

(*/ @: >: @: i. "0) 20

i.ってのが, range(x), >:がインクリメント, */が全部かける.

@:は関数(動詞って言うんだけど)の接続詞.

"0はおまじないと思って...



これを再帰で書くとこうなる.

1:`(] * $:@<:)@.* 20

1以上の時は (] * $: @ <:) で計算される.

]は引数その物, *はそのまま掛け算, <:はディクリメント. $:ってのがSelf Referenceで, その定義は次の通り.

$: denotes the longest verb that contains it.

一番長いってので, 曖昧性が消されてる.

J面白いなぁ.