几天前,我在“C# 中的匿名递归”上浏览了这个网站。这篇文章的主旨是以下代码在 C# 中不起作用:
Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
然后,本文详细介绍了如何使用currying和Y-combinator回到 C# 中的“匿名递归”。这很有趣,但恐怕对我的日常编码来说有点复杂。至少在这个时候...
我喜欢自己看东西,所以我打开了 Mono CSharp REPL并输入了那行。没有错误。于是,我进入了fib(8);
。令我大吃一惊的是,它奏效了!REPL 回复了21
!
我想这可能是 REPL 的一些神奇之处,所以我启动了“vi”,输入以下程序并编译它。
using System;
public class Program
{
public static void Main(string[] args)
{
int x = int.Parse(args[0]);
Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
Console.WriteLine(fib(x));
}
}
它也完美地构建和运行!
我在 Mac 上运行 Mono 2.10。我现在无法访问 Windows 机器,因此无法在 Windows 上的 .NET 上进行测试。
这是否也已在 .NET 上得到修复,或者这是 Mono 的静默功能?这篇文章是几年前的。
如果它只是 Mono,我等不及下一次工作面试,他们要求我用我选择的语言(Mono C#)编写一个 Fibinocci 函数,我必须提供警告,即 .NET 将无法工作。好吧,其实我可以等,因为我热爱我的工作。不过,有趣的是...
更新:
Mono 并没有真正进行“匿名”递归,因为它fib
用作命名委托。我的错。Mono C# 编译器假定分配前的null
值fib
是一个错误,如下所述。我说“编译器”是因为 .NET CLR 可以很好地运行生成的程序集,即使 .NET C# 编译器不会编译代码。
对于所有纳粹采访:
Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
可以替换为迭代版本:
Func<int, int> fib = n =>
{
int old = 1;
int current = 1;
int next;
for (int i = 2; i < n; i++)
{
next = current + old;
old = current;
current = next;
}
return current;
};
您可能希望这样做,因为递归版本在 C# 等语言中效率低下。有些人可能会建议使用记忆,但由于这仍然比迭代方法慢,他们可能只是个傻瓜。:-)
不过,在这一点上,这更像是函数式编程的广告(因为递归版本要好得多)。它确实与我最初的问题没有任何关系,但一些答案认为这很重要。