CoffeeScriptにおけるarguments.callee的な再帰の考察
題材として、以下のフィボナッチ数の計算関数を利用します。効率が悪いのはご愛嬌。
fib = (n) -> return 1 if n is 1 or n is 2 (fib n-1) + (fib n-2) alert fib 10
ここで「fib(10)の結果を取得したいが、これ以上fibを使わないため、出来ればfibを消すかローカル変数にしたい」と仮定します。
JavaScriptならarguments.calleeを用いれば
alert((function(n) { if (n == 1 || n == 2) return 1; arguments.callee(n-1) + arguments.callee(n-2); })(10));
このようにarguments.calleeを用いてfibすら宣言せずに書けます。
ちなみにStrictモードではarguments.calleeが非推奨なので「名前付き無名関数」を使うのが正攻法です。
alert((function fib(n) { if (n == 1 || n == 2) return 1; fib(n-1) + fib(n-2); })(10));
ということで、CoffeeScriptでもこんな感じで書こう、という試みです。
普通の解決策としては、素直に無名関数で囲う方法があります。
alert do -> fib = (n) -> return 1 if n is 1 or n is 2 (fib n-1) + (fib n-2) fib 10
もうちょっとスマートにすると
alert do -> (fib = (n) -> return 1 if n is 1 or n is 2 (fib n-1) + (fib n-2)) 10
という感じで書けますが、CoffeeScriptだとカッコで囲むのは何だかキモいですね。
そこで、doに渡す関数に、名前をつけ、さらにデフォルト引数を利用するのが今回の提案です。
alert do -> do fib = (n=10) -> return 1 if n is 1 or n is 2 (fib n-1) + (fib n-2)
do -> do
の部分が若干キモいですが、かなり自然になったのではないかと思います。
ちなみに、変換すると以下のコードが生成されます。
alert((function() { var fib; return (fib = function(n) { if (n === 1 || n === 2) { return 1; } return (fib(n - 1)) + (fib(n - 2)); })(10); })());
doのデフォルト引数が無名関数の引数になるのは(例えばdo (n=10) ->
は(function(n) {})(10);
)、マイナーながら使えるテクかもしれません。