Grand Central Dispatch のブロックでYコンビネータを書いてみた (3)
前回のまた続き。
これはYコンビネータなのか。
今度はYコンビネータに与える関数のカリー化を解いた。こうしたら再帰するために呼び出すブロックを記憶せずにすむ。そしてプログラム本体も、とても簡潔になった。
typedef int (^int2int)(int); // Haskell風に書くと、Int -> Int typedef int (^Yfunc)(int2int, int); // (Int -> Int, Int) -> Int int2int YCombinator(Yfunc f) { __block int2int y; y = Block_copy(^(int x) { return f(y, x); }); return y; } Yfunc fact = ^(int2int f, int x) { return x < 2 ? 1 : x * f(x - 1); }; int main(void) { int2int f = YCombinator(fact); printf("f(3) = %d\n", f(3)); Block_release(f); return 0; }
こうしてみてみると、factは普通の関数(ブロックでない)でもいいんじゃないかと思ってみたり。いや、それじゃ無名再帰にならないか。
役に立つのか?
こいつのintを全部idにしたら、オブジェクト全般に使えるぜ!・・・でもオブジェクト指向言語で再帰って、あまりお呼ばれではないイメージだしなぁ。それにGC必須になるし。
おまけ
フィボナッチ数を計算させてみた。
int2int f = YCombinator(^(int2int f, int x) { return x <= 1 ? x : f(x - 1) + f(x - 2); }); printf("fib(10) = %d\n", f(10));