Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handy mapping with n-adic functions #9905

Open
dlangBugzillaToGithub opened this issue May 18, 2011 · 1 comment
Open

Handy mapping with n-adic functions #9905

dlangBugzillaToGithub opened this issue May 18, 2011 · 1 comment

Comments

@dlangBugzillaToGithub
Copy link

bearophile_hugs reported this on 2011-05-18T18:20:16Z

Transfered from https://issues.dlang.org/show_bug.cgi?id=6034

CC List

Description

A very common operation is to map a n-adic function (like a triadic one) on n iterables. To do it in Haskell you use zipWith, example:

Prelude> let arr1 = [1, 2, 3]
Prelude> let arr2 = [10, 20, 30]
Prelude> let arr3 = [100, 200, 300]
Prelude> let foo x y z = x + y * z
Prelude> zipWith3 foo arr1 arr2 arr3
[1001,4002,9003]


In Python2.6 the normal map function is enough, it optionally accepts more than one iterable (in Python3 map does the same, but it's lazy):


def foo(x, y, z):
    return x + y * z

arr1 = [1, 2, 3]
arr2 = [10, 20, 30]
arr3 = [100, 200, 300]
print map(foo, arr1, arr2, arr3)


But in D2+Phobos you need to write awkward code:

import std.stdio, std.algorithm, std.range;

int foo(int x, int y, int z) {
    return x + y * z;
}

void main() {
    auto arr1 = [1, 2, 3];
    auto arr2 = [10, 20, 30];
    auto arr3 = [100, 200, 300];
    auto r = map!((t){ return foo(t.tupleof); })(zip(arr1, arr2, arr3));
    writeln(r);
}

Output:
[1001, 4002, 9003]


Unlike Python currently std.algorithm.map() is designed to optionally accept more than one function, and return a tuple. It's sometimes useful to do this, but in my experience the semantics of the Python map is useful more often. So I'd like the semantics of D map to become similar to the semantics of the Python3 map. In this case the D code becomes something like:

auto r = map!foo(arr1, arr2, arr3);

If this is not possible, or not desired (or it leads to too much complex Phobos code), then I suggest to introduce a new function in std.algorithm (or even in std.range) that acts like the Haskell zipWith. In this case the D code becomes something like:

auto r = zipWith!foo(arr1, arr2, arr3);
@dlangBugzillaToGithub
Copy link
Author

bearophile_hugs commented on 2011-05-18T18:32:21Z

An example usage for triadic map/zipWith (this is Euler problem 18, http://projecteuler.net/index.php?section=problems&id=18 ):


import std.stdio, std.algorithm, std.range, std.array;

auto reversed(R)(R range) {
    auto result = array(range);
    result.reverse;
    return result;
}

int f(int x, int y, int z) { return x + max(y, z); }

int[] g(int[] xs, int[] ys) {
    return array(map!((t){ return f(t.tupleof); })(zip(ys, xs[0..$-1], xs[1..$])));
}

void main() {
    auto tri = [[75],
                [95,64],
                [17,47,82],
                [18,35,87,10],
                [20, 4,82,47,65],
                [19, 1,23,75, 3,34],
                [88, 2,77,73, 7,63,67],
                [99,65, 4,28, 6,16,70,92],
                [41,41,26,56,83,40,80,70,33],
                [41,48,72,33,47,32,37,16,94,29],
                [53,71,44,65,25,43,91,52,97,51,14],
                [70,11,33,28,77,73,17,78,39,68,17,57],
                [91,71,52,38,17,14,91,43,58,50,27,29,48],
                [63,66, 4,68,89,53,67,30,73,16,69,87,40,31],
                [ 4,62,98,27,23, 9,70,98,73,93,38,53,60, 4,23]];

    writeln(reduce!g(reversed(tri))[0]);
}


In Haskell it becomes:


problem_18 = head $ foldr1 g tri
  where
    f x y z = x + max y z
    g xs ys = zipWith3 f xs ys $ tail ys
    tri = [...]



In Python2.6 (this is not pythonic code):

f = lambda x, y, z: x + max(y, z)
g = lambda xs, ys: map(f, ys, xs[:-1], xs[1:])
print reduce(g, reversed(tri))[0]


With a n-way map the function g() becomes (this uses the array version of map):

int[] g(int[] xs, int[] ys) {
    return amap!f(ys, xs[0..$-1], xs[1..$]);
}


With a zipWith the function g() becomes:

int[] g(int[] xs, int[] ys) {
    return array(zipWith!f(ys, xs[0..$-1], xs[1..$]));
}

@LightBender LightBender removed the P4 label Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants