Bug 7957 – std.functional untuple/untupleReversed too
Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-04-20T18:08:34Z
Last change time
2017-10-15T14:27:46Z
Assigned to
RazvanN
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2012-04-20T18:08:34Z
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]
Comment #1 by razvan.nitu1305 — 2017-10-13T09:12:31Z