我必须警告您,我正在尝试修复您的尝试,而不是制作富有成效的代码。另外,这个解决方案可以让我们的大脑爆炸,也许电脑也可以。
在您的第一个片段中,您尝试调用递归您的字段或局部变量,这是不可能的。相反,我们可以尝试使用可能更相似的 lambda。我们从教堂知道,这也是不可能的,至少以传统方式。Lambda 表达式未命名;你不能叫他们的名字(在实现内部)。但是您可以使用定点进行递归。如果您头脑清醒,则很有可能不知道那是什么,无论如何,在继续此之前,您应该尝试一下此链接。
fix :: (a -> a) -> a
fix f = f (fix f)
这将是 c# 实现(这是错误的)
A fix<A>(Func<A,A> f) {
return f(fix(f));
}
为什么错了?因为 fix(f) 代表了一个漂亮的 stackoverflow。所以我们需要让它变得懒惰:
A fix<A>(Func<Func<A>,A> f) {
return f(()=>fix(f));
}
现在很懒!实际上你会在下面的代码中看到很多这样的内容。
在您的第二个片段和第一个片段中,您遇到的问题是 Enumerable.Concat 的第二个参数不是惰性的,并且您将遇到 stackoverflow 异常或理想主义方式的无限循环。所以让我们让它变得懒惰。
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实现,但这就是为什么如果你想用两种语言编写类似的解决问题的方法看起来如此不同的想法