www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 7957] New: std.functional untuple/untupleReversed too

http://d.puremagic.com/issues/show_bug.cgi?id=7957

           Summary: std.functional untuple/untupleReversed too
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: Phobos
        AssignedTo: nobody puremagic.com
        ReportedBy: bearophile_hugs eml.cc



I suggest two functions like this in Phobos. Maybe a more correct name for them
is apply/applyReversed, but apply() is probably meant to be more flexible.

The purpose of untuple is to apply the items of a tuple as arguments to a
function, inside map/filter. So instead of this:
t => foo2(t.tupleof)

You write a less complex:
untuple!foo2

Python has the itertools.starmap function to perform a lazy map with untuple
(apply) too, but it lacks a starfilter:
http://docs.python.org/library/itertools.html#itertools.starmap

untupleReversed does a little more, because it also reverses the tuple items
before feeding them to the foo2 function. This is often useful for 2-tuples,
it's related to (but not the same as) the "flip" function used in Haskell:

-- |  'flip' f  takes its (first) two arguments in the reverse order of  f .
flip                    :: (a -> b -> c) -> b -> a -> c
flip f x y              =  f y x

It's an "adapter" between a range of tuples and an already written function
that takes flipped or reversed arguments.

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

import std.algorithm, std.range, std.stdio, std.functional,
       std.typecons, std.traits;

// To apply a given callable to a Tuple (alternative name: "apply").
template untuple(alias callable) {
    auto untuple(Tup)(Tup t)
    if (isTuple!Tup && ParameterTypeTuple!callable.length == Tup.length) {
        return callable(t.tupleof);
    }
}

void main() {
    auto a1 = "abcd"d;
    auto a2 = [10, 20, 30, 40];
    auto a3 = [100, 200, 300, 400];

    static int foo2(dchar a, int b) { return a + b; }
    zip(a1, a2).map!(t => foo2(t.tupleof))().writeln();

    zip(a1, a2).map!(untuple!foo2)().writeln();

    static int foo3(dchar a, int b, int c) { return a + b + c; }
    zip(a1, a2, a3).map!(untuple!foo3)().writeln();
}


Output:

[107, 118, 129, 140]
[107, 118, 129, 140]
[207, 318, 429, 540]

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

One version that also reverses the tuple items:


import std.algorithm, std.range, std.stdio, std.typecons, std.traits,
       std.typetuple, std.string, std.conv;

// _genIndexes("foo", 3) ==> "foo[2], foo[1], foo[0]"
private string _genIndexes(string name, size_t start) {
    string[] result;
    for (int i = start - 1; i >= 0; i--)
        result ~= name ~ "[" ~ text(i) ~ "]";
    return result.join(", ");
}

// To apply a given callable to a Tuple (alternative name: "apply"),
// but flipping the tuple items.
template untupleReversed(alias callable) {
    auto untupleReversed(Tup)(Tup t)
    if (isTuple!Tup && ParameterTypeTuple!callable.length == Tup.length) {
        mixin("return callable(" ~ _genIndexes("t", Tup.length) ~ ");");
    }
}

void main() {
    auto pairs = zip([10, 20, 30, 40], [100, 200, 300, 400]);
    static int div2(int a, int b) { return a / b; }

    pairs.map!(t => div2(t[1], t[0]))().writeln();
    pairs.map!(untupleReversed!div2)().writeln();
}


Output:

[10, 10, 10, 10]
[10, 10, 10, 10]

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 20 2012