我冲浪到 网站几天前在"C#中的匿名递归"。文章的主旨是下面的代码在C中是行不通的#:

Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;

然后,本文详细介绍了如何使用 柯里奇Y-组合器 回到C#中的"匿名递归"。这很有趣,但对于我的日常编码来说,这是一个有点复杂的问题。至少在这一点上。..

我喜欢自己看东西,所以我打开了单声道 CSharp REPL 进入了那一行。没有错误。于是,我进入 fib(8);.令我非常惊讶的是,它成功了!代表回答说 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上运行Mono2.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#这样的语言中效率低下。有些人可能会建议使用 记忆化 但是,由于这仍然比迭代方法慢,因此它们可能只是wankers。:-)

不过,在这一点上,这更像是函数式编程的广告(因为递归版本更好)。它真的与我原来的问题没有任何关系,但一些答案认为这很重要。

有帮助吗?

解决方案

这是一个 臭虫 在Mono编译器中。它违反了第§12.3.3节 规格说明.变量 n.纤维,纤维 不能在变量初始化器中使用,因为它不是肯定分配的。

其他提示

正如我在上面的评论中指出的那样,如果Mono这样做,那么他们有一个错误。规范很清楚,这应该被检测为错误。这个bug当然大多是无害的,而且大多数时候都是你想做的。我们已经考虑改变规则,使这种递归合法;基本上,我们必须在规范中添加一个特殊情况,说明这个狭义的案例是合法的。不过,这从来都不是一个足够高的优先级。

有关这个问题的更多想法,请参阅我关于这个问题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2006/08/18/706398.aspx

顺便说一句,我不会雇用任何在接受采访时给我一个直接递归实现fib的人。它是 效率低下;它的运行时间与其输出的大小成正比,fib呈指数增长。为了使其高效使用递归 与记忆, ,或者实施明显的迭代解决方案。

试试这个。..

Func<int, int> fib = null;
fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n; 

...问题是,当您尝试在上述方法中使用fib时,fib未被定义,因此静态分析器报告编译器错误。

看来,在我的兴奋中,我从根本上错了。.NET和Mono都没有像原始文章所说的那样提供"匿名递归"。你不能四处走动 fib 作为一个独立的实体。

在Mono C#REPL中查看以下序列:

csharp> Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n; 
csharp> fibCopy = fib;
csharp> fib(6);
8
csharp> fibCopy(6);
8
csharp> fib = n => n * 2;
csharp> fib(6);
12
csharp> fibCopy(6);
18

这是因为:

fib = n => n * 2;
fibCopy = n > 1 ? fib(n - 1) + fib(n - 2) : n;

换句话说,

fibCopy = n > 1 ? (n - 1) * 2 + (n - 2) * 2 : n;    // at the moment

显然, fibCopy 只是指着当前的定义 fib (代表)而不是自己。所以Mono实际上只是预先分配一个值 nullfib 在初始赋值期间使赋值有效。

我更喜欢不必申报的方便 null 所以我确实喜欢这种行为。尽管如此,这并不是原始文章所说的。

在微软的C#编译器中,只有在您第一次设置时才会工作 fibnull.

否则,它会给出错误,因为 fib 在分配之前使用。
Mono的编译器足够"聪明"以避免此错误(换句话说,它违反了官方规范)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top