enumerate, and some improvements

bearophile via Digitalmars-d digitalmars-d at puremagic.com
Wed Sep 24 04:32:02 PDT 2014


Jakob Ovrum and others have added an enumerate() function to 
Phobos:

https://issues.dlang.org/show_bug.cgi?id=5550
https://github.com/D-Programming-Language/phobos/pull/1866


A problem with enumerate() (present in other ranges too):

void main() {
   import std.stdio, std.range;
   auto A = [0, 0, 0, 0];
   // A trap: the compiler here accepts ref but it's ignored.
   foreach (immutable i, ref x; A.enumerate)
     x = i;
   writeln(A);
}


Outputs:

[0, 0, 0, 0]

------------------

A feature I'd like in Phobos is an optional "key" function for 
the min/max functions. An example, compared to using "minPos" 
(https://d.puremagic.com/issues/show_bug.cgi?id=4705 ):


void main() {
   import std.stdio, std.algorithm, std.range;

   string[] data = ["red", "hello", "yes", "no", "roger", "bud"];

   data
   .minPos!q{ a.walkLength > b.walkLength }
   .front
   .writeln;

   // Proposed:
   data.reduce!(max!q{ a.walkLength }).writeln;
}

The optional key function for max/min functions has some 
advantages: the code is simpler, shorter, and it computes 
walkLength only once for each string (so it's faster if the key 
function is nontrivial and gives less surprises if the given 
range is not pure).

------------------

A range I'd like in Phobos is std.range.pairwise:
https://d.puremagic.com/issues/show_bug.cgi?id=6788

It's different from cartesianProduct and zip, it generates just a 
triangle of pairs:

void main() {
   import std.stdio, std.typecons;
   auto data = [1, 2, 3, 4];
   foreach (tup; data.pairwise)
     writeln(tup);
   foreach (i, x1; data)
     foreach (x2; data[i + 1 .. $])
       writeln(tuple(x1, x2));
}

It should print:

Tuple!(int, int)(1, 2)
Tuple!(int, int)(1, 3)
Tuple!(int, int)(1, 4)
Tuple!(int, int)(2, 3)
Tuple!(int, int)(2, 4)
Tuple!(int, int)(3, 4)


So:

   foreach (tup; data.pairwise)

is similar to:

   foreach (i, x1; data)
     foreach (x2; data[i + 1 .. $])
       auto tup = tuple(x1, x2)

Such nested iteration is a common need.

------------------

I'd like "zip" to optionally accept a function/constructor:
https://d.puremagic.com/issues/show_bug.cgi?id=8715

Two usage examples:

void main() {
   import std.range;
   auto r1 = zip!q{a + b}([1,2,3], [10,20,30]);
   static struct Vec { int x, y; }
   auto r2 = zip!Vec([1,2,3], [10,20,30]);
}

This can be done with zip+map but this is shorter and more 
efficient. It's similar to the Haskell function zipWith.

------------------

And I'd like map/filter to accept arrays and associative arrays, 
and not just functions:

void main() {
   import std.algorithm: map;
   auto keys = [1, 2, 1, 1, 1];
   auto a = [0, 10, 20];
   auto r1 = map!(k => a[k])(keys); // OK
   auto r2 = map!a(keys);           // Error
   auto aa = [1: 10, 2: 20];
   auto r3 = map!(k => aa[k])(keys); // OK
   auto r4 = map!aa(keys);           // Error
}


This is a common idiom in Clojure. Arrays and associative arrays 
can be seen as functions defined by an enumeration of 
input-outputs.

Bye,
bearophile


More information about the Digitalmars-d mailing list