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 は引数として渡され、必要な場合にのみ次の反復で呼び出されます。