Comment #0 by bearophile_hugs — 2012-09-23T15:52:06Z
I suggest to add the higher order function "zipWith" to Phobos, similar to the Haskell function present in the Prelude:
Prelude> zipWith (+) [1,2,3] [10,20,30]
[11,22,33]
Prelude> data Vec = Vec Int Int deriving (Show)
Prelude> zipWith Vec [1,2,3] [10,20,30]
[Vec 1 10,Vec 2 20,Vec 3 30]
As you see it's handy in two use cases: the first case is a zip that applies a given function to the pairs. The other important use case is when you don't need zip() to build an iterable of generic tuples, but you want it to build a range of a specific given struct/tuple.
The Haskell code in D using the current Phobos:
import std.algorithm, std.range;
void main() {
auto r1 = zip([1,2,3], [10,20,30]).map!(p => p[0] + p[1])();
static struct Vec { int x, y; }
auto r2 = zip([1,2,3], [10,20,30]).map!(p => Vec(p.tupleof))();
}
With a zipWith() the code becomes simpler and more readable:
import std.range;
void main() {
auto r1 = zipWith!q{a + b}([1,2,3], [10,20,30]);
static struct Vec { int x, y; }
auto r2 = zipWith!Vec([1,2,3], [10,20,30]);
}
Comment #2 by bearophile_hugs — 2012-09-23T17:48:57Z
A simpler alternative idea is to just extend zip() to optionally accept a function/constructor (it's also good for map() to accept a struct name):
import std.range;
void main() {
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]);
}
Comment #3 by timon.gehr — 2012-09-23T18:04:46Z
(In reply to comment #2)
> A simpler alternative idea is to just extend zip() to optionally accept a
> function/constructor (it's also good for map() to accept a struct name):
>
> import std.range;
> void main() {
> 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]);
> }
I prefer this. 'zip' would become 'zipWith' with a default template argument of
'tuple'. (map already accepts a struct with an appropriate constructor as that
fits the 'callable' definition.)
Comment #4 by bearophile_hugs — 2012-10-12T16:22:58Z
(In reply to comment #3)
> (map already accepts a struct with an appropriate constructor as that
> fits the 'callable' definition.)
Right. But this shows a different limit. I think code like this is accepted in Clojure:
import std.algorithm: map;
void main() {
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
}
Comment #5 by bearophile_hugs — 2012-12-13T02:53:58Z