Re: Why not allow elementwise operations on tuples?

2023-01-19 Thread Sergei Nosov via Digitalmars-d-learn

On Wednesday, 18 January 2023 at 16:42:00 UTC, JG wrote:
I guess such a method wouldn't be particularly generic since a 
tuple does not need to consist of types that have the same 
operations e.g. Tuple!(int,string) etc


That's where `areCompatibleTuples` function comes in!


Re: Why not allow elementwise operations on tuples?

2023-01-18 Thread JG via Digitalmars-d-learn

On Monday, 16 January 2023 at 08:30:15 UTC, Sergei Nosov wrote:

On Friday, 13 January 2023 at 15:27:26 UTC, H. S. Teoh wrote:

[...]


Yeah, that's clear that such an implementation is rather 
straightforward. Although, I'm a bit confused with your 
implementation - 1. it doesn't seem to use tuples behind the 
scenes despite your claim (it uses static array) 2. `alias impl 
this;` introduces some unexpected interactions (e.g. `~` and 
`toString` are "intercepted" by the array implementation and 
yield "wrong" results).


Anyway, my original question was primarily about reasoning - 
why there's no implementation specifically for `std.Tuple`? If 
it's a "feature, not a bug" - what's the best way to provide an 
implementation on the client side?


I guess such a method wouldn't be particularly generic since a 
tuple does not need to consist of types that have the same 
operations e.g. Tuple!(int,string) etc


Re: Why not allow elementwise operations on tuples?

2023-01-16 Thread Sergei Nosov via Digitalmars-d-learn

On Friday, 13 January 2023 at 15:27:26 UTC, H. S. Teoh wrote:
On Fri, Jan 13, 2023 at 02:22:34PM +, 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.


Yeah, that's clear that such an implementation is rather 
straightforward. Although, I'm a bit confused with your 
implementation - 1. it doesn't seem to use tuples behind the 
scenes despite your claim (it uses static array) 2. `alias impl 
this;` introduces some unexpected interactions (e.g. `~` and 
`toString` are "intercepted" by the array implementation and 
yield "wrong" results).


Anyway, my original question was primarily about reasoning - why 
there's no implementation specifically for `std.Tuple`? If it's a 
"feature, not a bug" - what's the best way to provide an 
implementation on the client side?


Re: Why not allow elementwise operations on tuples?

2023-01-13 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Jan 13, 2023 at 02:22:34PM +, 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