Yコンビネータとは何ですか? [closed] 質問する

Yコンビネータとは何ですか? [closed] 質問する

Y コンビネータは、コンピュータ サイエンスの「関数型」の面から見た概念です。ほとんどのプログラマーは、コンビネータについて聞いたことがあるとしても、ほとんど知りません。

  • Yコンビネータとは何ですか?
  • コンビネータはどのように機能しますか?
  • それらは何に役立つのでしょうか?
  • それらは手続き型言語で役立ちますか?

ベストアンサー1

Y コンビネータは、関数自体を参照できない場合に再帰を可能にする「関数」(他の関数を操作する関数)です。コンピュータ サイエンスの理論では、再帰を一般化し、その実装を抽象化して、問題の関数の実際の動作から分離します。再帰関数にコンパイル時に名前を付ける必要がないという利点は、一種のボーナスです。=)

これは、サポートしている言語に適用されます。ラムダ関数表現ラムダの ベースの性質は、通常、名前で自分自身を参照できないことを意味します。そして、変数を宣言し、それを参照し、次にラムダをそれに割り当てて自己参照ループを完了するという方法でこの問題を回避することは脆弱です。ラムダ変数はコピーされ、元の変数が再割り当てされ、自己参照が壊れます。

Yコンビネータは実装が面倒で、多くの場合、静的型付け言語(手続き型言語多くの場合、型制約により、コンパイル時に問題の関数の引数の数を知る必要があるため、y コンビネータは使用する必要はありません。つまり、必要な引数の数に応じて y コンビネータを記述する必要があります。

以下は、C# での Y-Combinator の使用方法と動作の例です。

Y コンビネータを使用すると、再帰関数を構築するための「珍しい」方法が必要になります。まず、関数を、それ自体ではなく、既存の関数を呼び出すコードとして記述する必要があります。

// Factorial, if func does the same thing as this bit of code...
x == 0 ? 1: x * func(x - 1);

次に、それを呼び出す関数を受け取り、それを実行する関数を返す関数に変換します。これは、1 つの関数を受け取り、それに対して操作を実行して別の関数を生成するため、関数と呼ばれます。

// A function that creates a factorial, but only if you pass in
// a function that does what the inner function is doing.
Func<Func<Double, Double>, Func<Double, Double>> fact =
  (recurs) =>
    (x) =>
      x == 0 ? 1 : x * recurs(x - 1);

これで、関数を受け取り、階乗に似た別の関数を返す関数ができました。ただし、この関数は、自分自身を呼び出すのではなく、外側の関数に渡された引数を呼び出します。これを階乗にするにはどうすればよいでしょうか。内側の関数を自分自身に渡します。Y-Combinator は、永続的な名前を持つ関数にすることでこれを実現し、再帰を導入できます。

// One-argument Y-Combinator.
public static Func<T, TResult> Y<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> F)
{
  return
    t =>  // A function that...
      F(  // Calls the factorial creator, passing in...
        Y(F)  // The result of this same Y-combinator function call...
              // (Here is where the recursion is introduced.)
        )
      (t); // And passes the argument into the work function.
}

階乗がそれ自体を呼び出すのではなく、階乗が階乗ジェネレーター (Y-Combinator への再帰呼び出しによって返される) を呼び出します。そして、t の現在の値に応じて、ジェネレーターから返される関数は、t - 1 でジェネレーターを再度呼び出すか、単に 1 を返して再帰を終了します。

複雑で不可解ですが、実行時にすべて明らかになります。その動作の鍵は「遅延実行」と、再帰を 2 つの関数に分割することです。内部の F は引数として渡され、必要な場合にのみ次の反復で呼び出されます。

おすすめ記事