Array operations -- not actually so difficult
Sean Kelly
sean at f4.ca
Fri Dec 15 11:42:39 PST 2006
Bill Baxter wrote:
> Don Clugston wrote:
>> Array operations are one of the very few 2.0 features that haven't
>> made it into 1.0. My experiments into expression templates suggest
>> that it might not be very complicated after all.
>>
>> Consider something like a dot product,
>>
>> real a[], b[], c[];
>> real d = dot(a+b, c);
>>
>> For performance, it would be better if the compiler actually *didn't*
>> evaluate a+b. Instead, that should compile to:
>>
>> real sum = 0;
>> for (int i=0; i<c.length; ++i) {
>> sum+=(a[i]+b[i])*c[i];
>> }
>> d=sum;
>>
>> In fact, we could create a fantastic library implementation of array
>> operations with a tiny bit of syntactic sugar:
>> instead of generating an error message for array operations, look for
>> an opXXX function, and treat it the same way the implicit array
>> properties work.
>
> How does that result in the above code for dot product?
> Looks to me like if all you have is calling opXXX functions the above
> dot would still become two loops with a tmp for the a+b.
Not necessarily. Consider:
struct AddExp(T, U)
{
T lhs;
U rhs;
static AddExp!(T,U) opCall( T lhs, U rhs )
in
{
assert( lhs.length == rhs.length );
assert( is( typeof(T[0]) == typeof(U[0]) ) );
}
body
{
AddExp!(T,U) exp;
exp.lhs = lhs;
exp.rhs = rhs;
return exp;
}
typeof(T[0]) opIndex( size_t i )
{
return lhs[i] + rhs[i];
}
typeof(T[0])[] opSlice( size_t b, size_t e )
{
typeof(T[0])[] sum = new typeof(T[0])[lhs.length];
for( size_t i = 0; i < lhs.length; ++i )
{
sum[i] = lhs[i] + rhs[i];
}
return sum;
}
size_t length()
{
return lhs.length;
}
}
AddExp!(T,U) opAdd(T, U)( T lhs, U rhs )
{
return AddExp!(T,U)( lhs, rhs );
}
T[] opAssign(T, U)( inout T[] lhs, U rhs )
in
{
assert( is( typeof(T[0]) == typeof(U[0]) ) );
}
body
{
lhs = rhs[0 .. $];
return lhs;
}
real dotp(T,U)( T lhs, U rhs )
{
real sum = 0.0;
assert( lhs.length == rhs.length );
for( size_t i = 0; i < lhs.length; ++i )
{
sum += lhs[i] * rhs[i];
}
return sum;
}
void main()
{
real[] a, b, c;
a ~= 1.0; a ~= 2.0;
b ~= 3.0; b ~= 4.0;
c ~= 5.0; c ~= 6.0;
// test opSlice
auto s = opAdd( a, b );
real[] r = s[0 .. s.length];
for( size_t i = 0; i < r.length; ++i )
{
printf( "%Lf = %Lf\n", r[i], s[i] );
}
// compound expression
real res = dotp( opAdd( a, b ), c );
printf( "\n%Lf\n", res );
}
prints:
4.000000 = 4.000000
6.000000 = 6.000000
56.000000
Thanks to the wonder of templates, a properly designed expression struct
could be evaluated as-is rather than needing to be converted to an array
before being passed to dotp(). If we were able to define free-floating
operators as per Don's suggestion, the above would be darn slick :-)
By the way, I originally tried slicing using the standard syntax "s[0 ..
$]" and got this error:
test.d(84): Error: undefined identifier __dollar
I imagine this is a known issue?
Sean
More information about the Digitalmars-d
mailing list