生産的なコードを作成するためではなく、あなたの試みを修正しようとしていることを警告する必要があります。また、このソリューションは、私たちの脳を爆発させるのに適しています。コンピューターも爆発する可能性があります。
最初のスニペットでは、フィールドまたはローカル変数を再帰的に呼び出そうとしましたが、それは不可能です。代わりに、それに似たラムダで試すことができます。私たちは教会から、少なくとも伝統的な方法ではそれが不可能であることを知っています。ラムダ式には名前がありません。それらを名前で呼び出すことはできません(実装内)。ただし、固定小数点を使用して再帰を行うことができます。あなたが正気であれば、それが何であるかを知らない可能性が高いですが、とにかく、これを続ける前にこのリンクを試してみてください.
fix :: (a -> a) -> a
fix f = f (fix f)
これは c# の実装になります (これは間違っています)。
A fix<A>(Func<A,A> f) {
return f(fix(f));
}
なぜ間違っているのですか?fix(f) は美しいスタック オーバーフローを表すためです。したがって、遅延させる必要があります。
A fix<A>(Func<Func<A>,A> f) {
return f(()=>fix(f));
}
今は怠惰です!実際、次のコードでこれを多く見ることができます。
2 番目のスニペットと最初のスニペットでは、Enumerable.Concat の 2 番目の引数が遅延していないという問題があり、理想的な方法でスタック オーバーフロー例外または無限ループが発生します。それでは怠惰にしましょう。
static IEnumerable<T> Concat<T>(IEnumerable<T> xs,Func<IEnumerable<T>> f) {
foreach (var x in xs)
yield return x;
foreach (var y in f())
yield return y;
}
これで、機能的な方法で試したことを実装するための「フレームワーク」全体ができました。
void play() {
Func<Func<Func<IEnumerable<int>>>, Func<IEnumerable<int>>> F = fibs => () =>
Concat(new int[] { 1, 1 },
()=> Enumerable.Zip (fibs()(), fibs()().Skip(1), (a,b)=> a + b));
//let's see some action
var n5 = fix(F)().Take(5).ToArray(); // instant
var n20 = fix(F)().Take(20).ToArray(); // relative fast
var n30 = fix(F)().Take(30).ToArray(); //this will take a lot of time to compute
//var n40 = fix(F)().Take(40).ToArray(); //!!! OutOfMemoryException
}
F 署名が地獄のように醜いことは知っていますが、これが haskell のような言語が存在する理由であり、F# でさえあります。C# は、この作業をこのように行うために作成されていません。さて、問題は、なぜ haskell がこのようなことを達成できるのかということです。あなたが何か言うたびに
a:: Int
a = 4
C# での最も類似した翻訳は次のとおりです。
Func<Int> a = () => 4
実際には Haskell の実装にもっと関与していますが、これが、問題を解決するための同様の方法を両方の言語で書きたい場合に非常に異なって見える理由です。