On Fri, Jan 13, 2023 at 02:22:34PM +0000, Sergei Nosov via Digitalmars-d-learn 
wrote:
> Hey, everyone!
> 
> I was wondering if there's a strong reason behind not implementing
> elementwise operations on tuples?
> 
> Say, I've decided to store 2d points in a `Tuple!(int, int)`. It would
> be convenient to just write `a + b` to yield another `Tuple!(int,
> int)`.

I've written a Vec type that implements precisely this, using tuples
behind the scenes as the implementation, and operator overloading to
allow nice syntax for vector arithmetic.

-----------------------------------snip------------------------------------
/**
 * Represents an n-dimensional vector of values.
 */
struct Vec(T, size_t n)
{
    T[n] impl;
    alias impl this;

    /**
     * Per-element unary operations.
     */
    Vec opUnary(string op)()
        if (is(typeof((T t) => mixin(op ~ "t"))))
    {
        Vec result;
        foreach (i, ref x; result.impl)
            x = mixin(op ~ "this[i]");
        return result;
    }

    /**
     * Per-element binary operations.
     */
    Vec opBinary(string op, U)(Vec!(U,n) v)
        if (is(typeof(mixin("T.init" ~ op ~ "U.init"))))
    {
        Vec result;
        foreach (i, ref x; result.impl)
            x = mixin("this[i]" ~ op ~ "v[i]");
        return result;
    }

    /// ditto
    Vec opBinary(string op, U)(U y)
        if (isScalar!U &&
            is(typeof(mixin("T.init" ~ op ~ "U.init"))))
    {
        Vec result;
        foreach (i, ref x; result.impl)
            x = mixin("this[i]" ~ op ~ "y");
        return result;
    }

    /// ditto
    Vec opBinaryRight(string op, U)(U y)
        if (isScalar!U &&
            is(typeof(mixin("U.init" ~ op ~ "T.init"))))
    {
        Vec result;
        foreach (i, ref x; result.impl)
            x = mixin("y" ~ op ~ "this[i]");
        return result;
    }

    /**
     * Per-element assignment operators.
     */
    void opOpAssign(string op, U)(Vec!(U,n) v)
        if (is(typeof({ T t; mixin("t " ~ op ~ "= U.init;"); })))
    {
        foreach (i, ref x; impl)
            mixin("x " ~ op ~ "= v[i];");
    }

    void toString(W)(W sink) const
        if (isOutputRange!(W, char))
    {
        import std.format : formattedWrite;
        formattedWrite(sink, "(%-(%s,%))", impl[]);
    }
}

/**
 * Convenience function for creating vectors.
 * Returns: Vec!(U,n) instance where n = args.length, and U is the common type
 * of the elements given in args. A compile-time error results if the arguments
 * have no common type.
 */
auto vec(T...)(T args)
{
    static if (args.length == 1 && is(T[0] == U[n], U, size_t n))
        return Vec!(U, n)(args);
    else static if (is(typeof([args]) : U[], U))
        return Vec!(U, args.length)([ args ]);
    else
        static assert(false, "No common type for " ~ T.stringof);
}

///
unittest
{
    // Basic vector construction
    auto v1 = vec(1,2,3);
    static assert(is(typeof(v1) == Vec!(int,3)));
    assert(v1[0] == 1 && v1[1] == 2 && v1[2] == 3);

    // Vector comparison
    auto v2 = vec(1,2,3);
    assert(v1 == v2);

    // Unary operations
    assert(-v1 == vec(-1, -2, -3));
    assert(++v2 == vec(2,3,4));
    assert(v2 == vec(2,3,4));
    assert(v2-- == vec(2,3,4));
    assert(v2 == vec(1,2,3));

    // Binary vector operations
    auto v3 = vec(2,3,1);
    assert(v1 + v3 == vec(3,5,4));

    auto v4 = vec(1.1, 2.2, 3.3);
    static assert(is(typeof(v4) == Vec!(double,3)));
    assert(v4 + v1 == vec(2.1, 4.2, 6.3));

    // Binary operations with scalars
    assert(vec(1,2,3)*2 == vec(2,4,6));
    assert(vec(4,2,6)/2 == vec(2,1,3));
    assert(3*vec(1,2,3) == vec(3,6,9));

    // Non-numeric vectors
    auto sv1 = vec("a", "b");
    static assert(is(typeof(sv1) == Vec!(string,2)));
    assert(sv1 ~ vec("c", "d") == vec("ac", "bd"));
    assert(sv1 ~ "post" == vec("apost", "bpost"));
    assert("pre" ~ sv1 == vec("prea", "preb"));
}

unittest
{
    // Test opOpAssign.
    auto v = vec(1,2,3);
    auto w = vec(4,5,6);
    v += w;
    assert(v == vec(5,7,9));
}

unittest
{
    int[4] z = [ 1, 2, 3, 4 ];
    auto v = vec(z);
    static assert(is(typeof(v) == Vec!(int,4)));
    assert(v == vec(1, 2, 3, 4));
}

unittest
{
    import std.format : format;
    auto v = vec(1,2,3,4);
    assert(format("%s", v) == "(1,2,3,4)");
}
-----------------------------------snip------------------------------------


T

-- 
Never ascribe to malice that which is adequately explained by incompetence. -- 
Napoleon Bonaparte

Reply via email to