【问题标题】:Have I implemented Y-combinator using C# dynamic, and if I haven't, what is it?我是否使用 C# 动态实现了 Y-combinator,如果没有,它是什么?
【发布时间】:2011-12-01 07:41:53
【问题描述】:

我的大脑似乎处于自虐状态,所以在被thisthisthis 淹没后,它想在C# 中进行一些DIY。

我想出了以下内容,我不认为是 Y 组合子,但它确实似乎设法使非递归函数递归, 不引用自身:

Func<Func<dynamic, dynamic>, Func<dynamic, dynamic>> Y = x => x(x);

所以给出这些:

Func<dynamic, Func<dynamic, dynamic>> fact = 
                  self => n => n == 0 ? 1 : n * self(self)(n - 1);
Func<dynamic, Func<dynamic, dynamic>> fib = 
                  self => n => n < 2 ? n : self(self)(n-1) + self(self)(n-2);

我们可以生成这些:

Func<dynamic, dynamic> Fact = Y(fact);
Func<dynamic, dynamic> Fib = Y(fib);

Enumerable.Range(0, 10)
          .ToList()
          .ForEach(i => Console.WriteLine("Fact({0})={1}", i, Fact(i)));

Enumerable.Range(0, 10)
          .ToList()
          .ForEach(i => Console.WriteLine("Fib({0})={1}", i, Fib(i)));

【问题讨论】:

    标签: c# dynamic functional-programming y-combinator


    【解决方案1】:

    不,那不是 Y 组合子;你只完成了一半。您仍然需要在应用它的“种子”功能中排除自我应用。也就是说,而不是这个:

    Func<dynamic, Func<dynamic, dynamic>> fact = 
                      self => n => n == 0 ? 1 : n * self(self)(n - 1);
    

    你应该有这个:

    Func<dynamic, Func<dynamic, dynamic>> fact = 
                      self => n => n == 0 ? 1 : n * self(n - 1);
    

    注意第二个定义中出现的self 与第一个定义中出现的两次相反。

    (编辑添加:)顺便说一句,由于您使用 C# 模拟 lambda 演算和按值调用评估,因此您想要的定点组合器是 the one often called Z, not Y

    (再次编辑以详细说明:)描述Y 的等式是这样的(推导参见the wikipedia page):

    Y g = g (Y g)
    

    但在大多数实用的编程语言中,您会在调用函数之前评估函数的参数。在编程语言社区中,这称为按值调用评估(不要与 C/C++/Fortran/etc 程序员区分“按值调用”与“按引用调用”与“按复制恢复调用”的方式相混淆等)。

    但如果我们这样做,我们会得到

    Y g = g (Y g) = g (g (Y g)) = g (g (g (Y g))) = ...
    

    也就是说,我们将所有的时间都花在构造递归函数上,而永远应用它。

    另一方面,在按名称调用评估中,您将函数(此处为 g)应用于未评估的参数表达式(此处为 (Y g))。但是如果g 就像fact 一样,它会在执行任何操作之前等待另一个参数。因此,我们将等待g 的第二个参数,然后再尝试进一步评估(Y g)——并且取决于函数的作用(即,如果它有一个基本情况),我们可能不需要评估(Y g)一点也不。这就是为什么Y 可用于名称调用评估。

    按值调用的解决方法是改变等式。我们想要的不是Y g = g (Y g),而是下面的等式:

    Z g = g (λx. (Z g) x)
    

    (我想我的方程是正确的,或者接近正确的。你可以计算一下,看看它是否符合Z的定义。)

    考虑这一点的一种方法是,我们不计算“整个递归函数”并将其交给g,而是将其递给一个函数,该函数将一次计算一点递归函数——并且仅当我们实际上需要更多的信息,以便我们可以将其应用于参数 (x)。

    【讨论】:

    • 我有机会详细说明你的 BTW 吗?它稍微超出了我的头顶(但大部分都是......)
    • 感谢您的进一步阐述 - 我对按值调用的解释感到困惑。我刚刚了解了您的递归示例(Z = f =&gt; f(f(f(f))); 将适用于我所包含的尽可能多的f...),现在正在努力进行下一步...
    • 是的,这是查看f的不动点的另一种方式:作为序列的极限{, f(⊥), f(f(⊥)), f(f(f(⊥))), ... },其中 是一个无法终止、崩溃或其他原因的函数。
    • 学术界大多数人的问题是他们不理解使用 X、Y 和 Z 作为示例变量名的含义。
    猜你喜欢
    • 2019-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-26
    • 1970-01-01
    • 2012-05-06
    相关资源
    最近更新 更多