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));