【问题标题】:Y-combinator in D?D中的Y组合子?
【发布时间】:2011-08-04 07:44:25
【问题描述】:

我正在尝试更好地学习 Y-combinator(我在 Scheme 中有点理解它)并在 D 2.0 中实现它,但我失败得很惨:

auto fact = delegate(uint delegate(uint) recurse)
{
    return delegate(uint n)
    {
        return n > 1 ? n * recurse(n - 1) : 1;
    };
};

fact(fact)(5);

这不起作用,原因很明显,我不能将fact 传递给fact(它的类型是什么?)。而且,我还需要fact 的名字才能传递给它自己,所以它无论如何也行不通,对吧?

但是...我该如何在 D 中实现 Y 组合器?

【问题讨论】:

  • 委托已经是引用类型,你可以放弃&
  • @BCS:很好,它最初是一种方法,但我忘了删除它。会修复。 :)

标签: functional-programming d lambda-calculus y-combinator


【解决方案1】:

查看详细说明here

【讨论】:

  • 似乎 D 缺少的关键特性(与 C# 相比)是能够在签名本身中使用委托别名的名称...
  • RosettaCode 的示例代码有效,所以可以说 D 缺少关键功能?
  • RosettaCode 的示例代码使用类型转换来规避丑陋的问题。可以将委托包装在结构中,因为结构可以具有类型递归依赖于结构类型的成员。但是 Mehrdad 有一个观点:由于编译器不允许大多数形式的别名递归,因此别名声明目前比必要的限制更多。 (例如:struct Foo(T){Bar* qux;} alias Foo!int Bar; // 编译错误 struct Foo(T){.Foo!int* qux;} // 有效)
【解决方案2】:

D(和 C/C++)的一个已知限制是处理类型化自引用的函数是不可能声明的(上次我检查过)。

我觉得这很讽刺,因为它限制了语法(函数原型的长度是无限的)或命名约定(同样的问题,但名称混乱),而不是任何语义或架构。

【讨论】:

  • +1 是的,这有点傻,我真的很困惑,试图围绕如何将函数传递给自己。知道他们是否打算解决这个问题? (例如,void foo(typeof(foo)) { } 给出 forward reference 错误。)
  • @Mahrdad:我不知道。 OTOH 一个简单的结构包装器解决了这个问题。
  • 顺便说一句,Haskell 也没有做到这一点,比如"Can't declare a type of infinite length"
【解决方案3】:

我不知道 D,但是对于大多数语言,当你尝试实现非递归时,你会遇到函数类型的问题(你的代码中还没有 Y-Combinator)。通常的解决方法是可以通过将类型分成几个部分来实现,这样就可以递归到类型中。

其他语言的一些例子:

  • 在 C 中,通常会编写一个包含函数指针的结构。然后可以将该结构用作参数。

  • 在 OCaml 中,可以添加一种包装函数类型的变体类型。这再次允许分离类型,因此可以进行类型递归。

如果您想要其他语言的示例,请查看this page

【讨论】:

    【解决方案4】:

    我在 D 中的通用 Y 组合器:

    import std.stdio, std.algorithm, std.range;
    auto Y(R,T...)(R delegate(T) delegate (R delegate(T)) f){
        struct F{R delegate(F,T) f;};
        return (R delegate(T)delegate(F) a){return a(F((F b,T i){return f(a(b))(i);}));
        }((F b){return (T n){return b.f(b,n);};});
    }
    void main(){
        auto factorial=Y((long delegate(long) self){return (long i){return i?self(i-1)*i:1;};});
        auto fibonacci=Y((int delegate(int) self){return (int i){return i<2?i:self(i-1)+self(i-2);};});
        auto ackermann=Y((long delegate(long,long) self){return (long n,long m){return n?m?self(n-1,self(n,m-1)):self(n-1,1):m+1;};});
        writeln(map!factorial(iota(0,20)));
        writeln(map!fibonacci(iota(0,20)));
        writeln(map!((a){return ackermann(a%4,a/4);})(iota(0,20)));
    }
    

    我从阶乘函数的简单递归定义开始并对其进行修改,直到我的代码看起来像这样。无限类型问题正在使用类型包装器 (struct F) 解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      • 2017-04-02
      • 2011-05-15
      相关资源
      最近更新 更多