On Tuesday, 2 October 2012 at 22:13:10 UTC, Timon Gehr wrote:
On 10/03/2012 12:11 AM, Timon Gehr wrote:
...
$ cat ixidbench.d
module main;
import std.stdio, std.algorithm, std.range, std.datetime;
enum MAX = 10_000_000_000UL;
void main() {
StopWatch sw;
sw.start;
auto sum1 = MAX.iota.map!(x => x * x).reduce!"a + b";
sw.stop;
sw.peek.msecs.writeln("msecs");
sum1.writeln;
sw.reset;
sw.start;
ulong sum2 = 0;
foreach(i;0..MAX)
sum2 += i * i;
sw.stop;
sw.peek.msecs.writeln("msecs");
sum2.writeln;
}
$ ldmd2 -O -release -inline ixidbench.d -ofixidbench
$ ./ixidbench
6528msecs
7032546979563742720
7518msecs
7032546979563742720
$ gdmd -O -release -inline ixidbench.d -ofixidbench
$ ./ixidbench
11250msecs
7032546979563742720
11233msecs
7032546979563742720
While fiddling with this I came across something that seems odd
in the behaviour of reduce and wondered if it's intended. It
rather limits the usefulness of reduce with UFCS to "a + b" and
"a - b".
Reduce works correctly when provided with a seed argument:
reduce!"a + b + 2"(0, [1,1,1]).writeln; // == 9 as expected
With UFCS I see no elegant way to reduce a chain with a more
sophisticated reduce argument than the two simple ones listed
above because the operation is not carried out on the first array
element, used as the seed. This means:
[1,1,1].reduce!"a + b + 2".writeln; // == 7
which is useless and could easily trip people up because the
intuitive expectation is that it will act like the seed example.
If I am wrong in that expectation what is a use case for the
seedless behaviour? Is there a way of writing the operation to
give the same answer as the seed answer?
The element in position 0 should surely have +2 added to itself,
it's only being the seed out of convenience isn't it? Instead we
get 1 + (1 + 2) + (1 + 2). The order of reduce's arguments,
(seed, range) prevent it being used with UFCS properly. If it
were (range, seed) then there would be no problem:
[1,1,1].reduce!"a + b + 2"(0).writeln; // == 9