代码之家  ›  专栏  ›  技术社区  ›  fadedbee

是否可以在D中通用地实现amb操作符?

  •  10
  • fadedbee  · 技术社区  · 14 年前

    是否可以在D中通用地实现amb操作符?

    http://www.haskell.org/haskellwiki/Amb
    http://www.randomhacks.net/articles/2005/10/11/amb-operator

    amb([1, 2]) * amb([3, 4, 5]) == amb([3, 4, 5, 6, 8, 10])
    amb(["hello", "world"]) ~ amb(["qwerty"]) == amb(["helloqwerty", "worldqwerty"])
    amb(["hello", "world"]) ~ "qwerty" == amb(["helloqwerty", "worldqwerty"])
    amb(["hello", "very long string"]).length = amb([5, 16])
    

    在最后两个例子中,确实需要将~and.length“提升”到amb“context”(单子?)中。在前两个示例中,运算符应仅应用于amb的内容。

    我做了一个简短的尝试,但在尝试提升包装类型的操作符/方法/属性时遇到了问题(在本例中,~和.length)。这应该如何在D中完成?

    克里斯。

    1 回复  |  直到 14 年前
        1
  •  12
  •   Peter Alexander    14 年前

    是的,这是可能的。这是我想到的。

    import std.range;
    import std.algorithm;
    import std.stdio;
    import std.functional;
    import std.math;
    import std.string;
    
    struct AmbRange(R1, R2, alias Op)
    {
    public:
        this(R1 _r1, R2 _r2) { r1 = _r1; r2 = r2c = _r2; }
    
        void popFront()
        {
            r2.popFront();
            if (r2.empty) { r2 = r2c; r1.popFront(); }
        }
    
        @property auto front() { return Op(r1.front, r2.front); }
        @property bool empty() { return r1.empty; }
    
    private:
        R1 r1;
        R2 r2, r2c;
    }
    
    struct Amb(R)
    {
        alias ElementType!(R) E;
    
    public:
        this(R r) { this.r = r; }
    
        auto opBinary(string op, T)(T rhs) if (!is(T U : Amb!(U)))
        {
            alias binaryFun!("a"~op~"b") Op;
            return map!((E e) { return Op(e, rhs); })(r);
        }
    
        auto opBinaryRight(string op, T)(T lhs) if (!is(T U : Amb!(U)))
        {
            alias binaryFun!("a"~op~"b") Op;
            return map!((E e) { return Op(lhs, e); })(r);
        }
    
        auto opBinary(string op, T)(T rhs) if (is(T U : Amb!(U)))
        {
            alias binaryFun!("a"~op~"b") Op;
            return AmbRange!(R, typeof(rhs.r), Op)(r, rhs.r);
        }
    
        auto opDispatch(string f, T ...)(T args)
        {
            mixin("return map!((E e) { return e."~f~"(args); })(r);");
        }
    
        auto opDispatch(string f)()
        {
            mixin("return map!((E e) { return e."~f~"; })(r);");
        }
    
    private:
        R r;
    }
    
    auto amb(R)(R r) { return Amb!R(r); }
    
    void main()
    {
        auto r1 = 2 * amb([1, 2, 3]);
        assert(equal(r1, [2, 4, 6]));
    
        auto r2 = amb(["ca", "ra"]) ~ "t";
        assert(equal(r2, ["cat", "rat"]));
    
        auto r3 = amb(["hello", "cat"]).length;
        assert(equal(r3, [5, 3]));
    
        auto r4 = amb(["cat", "pat"]).replace("a", "u");
        assert(equal(r4, ["cut", "put"]));
    
        auto r5 = amb([1, 2]) * amb([1, 2, 3]);
        assert(equal(r5, [1, 2, 3, 2, 4, 6]));
    }
    

    非常感谢BCS找出了解决二进制歧义的方法。

    Amb 是的,但我认为这样做最好。

    string 这些工作是在编译时完成的,所以在运行时没有解析代码或类似的东西——它的效率相当于用C手工编写代码。