Bug 7666 – A function to reverse the items of a tuple
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-03-07T11:13:00Z
Last change time
2014-02-15T03:21:05Z
Keywords
pull
Assigned to
andrej.mitrovich
Creator
bearophile_hugs
Comments
Comment #0 by bearophile_hugs — 2012-03-07T11:13:30Z
In some situations I'd like a function to reverse a tuple:
reversed(tuple(a,b)) == reversed(tuple(b,a))
reversed(tuple(a,b,c)) == reversed(tuple(c,b,a))
reversed(tuple(a,b,c,d)) == reversed(tuple(d,c,b,a))
Once built-in associative arrays have a byPair() method, that returns a 2-tuple, a reversed(tuple(a,b)) is useful when you need the pairs in reversed (value-key) order.
Comment #1 by andrej.mitrovich — 2012-10-27T15:07:05Z
First thing I could come up with, but it requires editing the Tuple!() struct. A public alias is added in Tuple struct:
alias Reverse!fieldSpecs revFieldSpecs;
And the internal FieldSpec struct is moved outside and made public. Then the following is possible:
auto reversed(T)(T tup)
if (isTuple!T)
{
static string getMixin(T)()
{
string[] result;
foreach (i; 0 .. T.Types.length)
{
result ~= xformat("tup.field[%d]", i);
}
return result.retro.join(", ");
}
static string getSpecs(T)()
{
string[] result;
foreach (spec; T.revFieldSpecs)
{
result ~= spec.Type.stringof;
result ~= xformat(`"%s"`, spec.name);
}
return result.join(", ");
}
enum str = xformat("return Tuple!(%s)(%s);", getSpecs!(T)(), getMixin!(T)());
mixin(str);
}
void main()
{
alias Tuple!(int, "index", string, "value") Entry;
Entry e;
e.index = 4;
e.value = "Hello";
auto rev = reversed(e);
assert(e.index == 4);
assert(e.value == "Hello");
writeln(e);
writeln(rev);
}
Prints:
Tuple!(int,"index",string,"value")(4, "Hello")
Tuple!(string,"value",int,"index")("Hello", 4)
If there is a non-invasive way of doing this it would be welcome.
Comment #2 by timon.gehr — 2012-10-27T15:59:04Z
(In reply to comment #1)
> ...
>
> If there is a non-invasive way of doing this it would be welcome.
import std.typecons, std.conv, std.range, std.algorithm;
auto reversed(T)(T t) if(isTuple!T){
return mixin(`tuple(`~iota(T.length).retro.map!(a=>text("t[",a,"]")).join(",")~`)`);
}
Comment #3 by timon.gehr — 2012-10-27T16:20:46Z
(In reply to comment #2)
> (In reply to comment #1)
> > ...
> >
> > If there is a non-invasive way of doing this it would be welcome.
>
With field names:
import std.typecons, std.conv, std.range, std.algorithm;
auto reversed(T)(T t) if(isTuple!T){
static if(is(T X:Tuple!A,A...)) alias A Spec;
static struct Local{
template Seq(T...){ alias T Seq; }
template RevSpec(A...){
enum num=A.length>1&&!is(A[1])?2:1;
static if(A.length) alias Seq!(RevSpec!(A[num..$]),A[0..num]) RevSpec;
else alias A RevSpec;
}
}
return mixin(`Tuple!(Local.RevSpec!Spec)(`~
iota(T.length).retro.map!(a=>text("t[",a,"]")).join(",")~
`)`);
}
Comment #4 by andrej.mitrovich — 2012-10-27T16:54:02Z
(In reply to comment #3)
> With field names
Interesting, the last time I saw this syntax:
static if(is(T X : Tuple!A, A...)) alias A Spec;
it was in the templates book (https://github.com/PhilippeSigaud/D-templates-tutorial), where Philippe mentions:
<<beg quote
For me, the main limitation is that template tuple parameters are not ac-
cepted. Too bad. See, imagine you use std.typecons.Tuple a lot. At one point,
you need a template to test if something is a Tuple!(T...) for some T which
can be 0 or more types. Though luck, is is a bit of a letdown there, as you
cannot do:
template isTuple(T)
{
static if (is(T tup == Tuple!(InnerTypes), InnerTypes...))
{
enum isTuple = true;
}
}
end quote>>
Maybe this was a compiler limitation in earlier versions.
Comment #5 by timon.gehr — 2012-10-27T18:10:59Z
(In reply to comment #4)
> ...
> Maybe this was a compiler limitation in earlier versions.
Yup, Kenji Hara fixed that recently.
Comment #6 by andrej.mitrovich — 2013-04-05T12:16:18Z
Got something better:
auto reversed(T)(T t)
if (isTuple!T)
{
static if (is(T : Tuple!A, A...))
alias RevTypes = Reverse!A;
Tuple!RevTypes result;
auto tup = t.tupleof;
result.tupleof = Reverse!tup;
return result;
}
void main()
{
auto tup = tuple(1, "2");
assert(tup.reversed == tuple("2", 1));
}
I'll make a pull shortly.
P.S. this is a compile-error: Reverse!(t.tupleof)
Might be worth filing this as a bug.
Comment #7 by andrej.mitrovich — 2013-04-05T12:22:29Z
Comment #9 by andrej.mitrovich — 2013-04-05T13:12:05Z
So sorry about this, I completely forgot about the string specs feature of Tuples. I guess I was a little sleepy. Will re-do a pull tomorrow.
Comment #10 by andrej.mitrovich — 2013-04-05T13:16:29Z
(In reply to comment #9)
> So sorry about this, I completely forgot about the string specs feature of
> Tuples. I guess I was a little sleepy. Will re-do a pull tomorrow.
Also as reported by Walter it seems we get frame pointer errors when trying to use it from within a unittest.
Comment #11 by andrej.mitrovich — 2013-04-06T01:07:30Z
Comment #13 by bearophile_hugs — 2014-02-15T03:21:05Z
The presence of this function solves the problem of choosing what's the right API for the Phobos-defined array.reverse: for API uniformity is should be like this one, so array.reverse should reverse the items in-place (unlike this Tuple.reverse) and return the reversed items, to be used in UFCS chains.