こんにちは、びしょ〜じょです。

さて、無名再帰といえば誰しも無駄にやりたくなってしまうものです。 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モジュール使われると絶対バグるのでやめて欲しいゾ👊

日程は未定です、人間がいっぱい居たほうが楽しいのでいっぱい来てください。