Luaの無名再帰
こんにちは、びしょ〜じょです。
さて、無名再帰といえば誰しも無駄にやりたくなってしまうものです。
JavaScriptならarguments.callee()
という関数があるし、関数をトップレベルで扱えるのなら不動点コンビネータを書いてみたり、fix()
関数がすでにあったり。
Luaももちろん関数をトップレベルで扱えるので不動点コンビネータで無名再帰を実装することができます。
print((function(f)
return (function(x)
return f(function(y)
return x(x)(y)
end)
end)(function(x)
return f(function(y)
return x(x)(y)
end)
end)
end)(function (f)
return function(n)
return n < 1 and n or n * f(n - 1)
end
end)(5))
MoonScriptで書いても良い。
print ((
(f) ->
((x) -> f (y) -> (x x) y)((x) -> f (y) -> (x x) y)
) (f) -> (n) -> n == 0 and 1 or n * f n - 1) 5
ところでdebug.getinfo()
という関数がある。知らなかった。
> debug.getinfo debug.getinfo
{
currentline = -1,
func = <function 1>,
istailcall = false,
isvararg = true,
lastlinedefined = -1,
linedefined = -1,
namewhat = "",
nparams = 0,
nups = 0,
short_src = "[C]",
source = "=[C]",
what = "C"
}
また、第1引数を関数ではなくcallerのレベルを渡すことで、そのレベルのcallerの情報を得ることができる。
レベルは、debug.getinfo()
自身を0として、その外側のクロージャーからだんだん大きくなっていく。
> (-> debug.getinfo 1)!
{
currentline = 2,
func = <function 1>,
istailcall = true,
isvararg = false,
lastlinedefined = 3,
linedefined = 1,
namewhat = "",
nparams = 0,
nups = 1,
short_src = '[string "tmp"]',
source = "tmp",
what = "Lua"
}
返ってくるtableのfunc
は指している関数そのものだ。
もうなにがやりたいかわかりますね。
print ((n) -> n == 0 and 1 or n * (debug.getinfo 1).func n - 1) 5
公式にもあるように、
You should exert care when using this library.
Several of its functions violate basic assumptions about Lua code ...
なのでご利用は計画的に。
宣伝
tsukuba.luaというLuaやLua以外に関する発表やってワチャワチャするやつやります。 Lua VM Bytecodeの最適化の話をする予定です。確かに最適化に当たってdebugモジュール使われると絶対バグるのでやめて欲しいゾ👊
日程は未定です、人間がいっぱい居たほうが楽しいのでいっぱい来てください。