10

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])

最後の 2 つの例では、~ と .length を amb 'context' (モナド?) に 'lifting' する必要があります。最初の 2 つの例では、演算子は AMB のコンテンツに適用する必要があります。

簡単に試してみましたが、ラップされた型の演算子/メソッド/プロパティ (この例では *、~、および .length) を持ち上げようとすると問題が発生します。これはDでどのように行うべきですか?

ありがとう、

クリス。

4

1 に答える 1

12

はい、可能です。これが私が思いついたものです。

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]));
}

binaryOpのあいまいさを解決する方法を理解してくれたBCSに感謝します。

2つの間のバイナリ操作の結果をトラバースするための新しい範囲を作成する必要がありましたが、Ambとにかくそれが最もうまくいくと思います。

Dに不慣れで、好奇心旺盛な人にとっては、すべてのstring作業はコンパイル時に行われるため、実行時にコードを解析する必要はありません。Cで手動でコーディングするのと同じくらい効率的です。

于 2010-08-31T15:49:15.167 に答える