On 07/03/13 19:10, Brad Anderson wrote:
> On Wednesday, 3 July 2013 at 16:35:18 UTC, Artur Skawina wrote:
>> On 07/03/13 18:29, Brad Anderson wrote:
>>> On Wednesday, 3 July 2013 at 11:54:39 UTC, Artur Skawina wrote:
>>>> On 07/03/13 02:22, Brad Anderson wrote:
>>>>> C++11's std::tuple includes a function std::tie that takes references to
>>>>> the arguments and returns a tuple that maintains the references to the
>>>>> arguments.
>>>>>
>>>>> Along with the usual cases where you'd want reference semantics it also
>>>>> enables this interesting construct for unpacking tuples.
>>>>>
>>>>> int a, b;
>>>>> tie(a, b) = make_tuple(1, 2);
>>>>>
>>>>> assert(a == 1 && b == 2);
>>>>>
>>>>> Is there any way to do something similar with std.typecons.Tuple?
>>>>
>>>> Well, aliases can be used to get a similar effect.
>>>>
>>>> template tie(A...) { alias tie = A; }
>>>> tie!(a, b) = tuple(1, 2);
>>>
>>> That won't work. a and b aren't held as references (also you passed them
>>> as type parameters :P).
>>
>> Try it...
>>
>> And, yes, the fact that 'A...' template parms accept symbols
>> is not exactly obvious. But it's much more useful that way.
>>
>> artur
>
> Huh, I had no idea you could do something like that. I stand corrected.
> Thanks.
>
> That does get tie = working but doesn't work if you want to pass it around
> which is actually more at the heart of what I'm interested in.
>
> To get straight to the point, I was playing around with implementing
> bearophile's enumerate() feature request (something I've wanted myself).
> Both his posted solution [1] and my own quick testing hack (auto
> enumerate(Range)(Range r) { return zip(sequence!"n"(), r); }) lose the
> ability to do ref elements in foreach that can modify the source range:
>
> ---
> auto a = ["a", "b", "c"];
> foreach(i, ref item; a.enumerate())
> item = to!string(i);
>
> assert(a == ["0", "1", "2"]); // fails, a is still ["a", "b", "c"]
> ---
>
> They don't work because both the tuples he returns from front and the tuples
> zip creates aren't references to the originals so you are just changing the
> copy stored in the tuple.
>
> Something like "alias RefIntTuple = Tuple!(ref int, ref int);" gives a
> compiler error (Error: expression expected, not 'ref').
>
> 1. http://d.puremagic.com/issues/show_bug.cgi?id=5550#c2
D does not yet have proper ref types, which means a lot of things
are not possible, at least not directly. And a lot of hacks are
required to achieve certain effects. Anyway, the following seems
to work - it's bearophiles code from the mentioned bugzilla entry
with some tweaks.
It's dirty enough, so I shouldn't be posting this on a 'learn'
ML... It's meant more as an illustration of the language deficiencies.
Please do not use anything like this. :)
import std.stdio, std.algorithm, std.range, std.typecons, std.traits,
std.array;
struct RefHack(T) {
T* ptr;
ref get() @property { return *ptr; }
alias get this;
}
auto refHack(T)(ref T a) { return RefHack!T(&a); }
struct Enumerate(R) {
R r;
int i;
@property bool empty() { return r.empty; }
@property Tuple!(typeof(this.i), typeof(refHack(r.front))) front() {
return typeof(return)(i, refHack(r.front));
}
void popFront() {
this.r.popFront();
this.i++;
}
}
Enumerate!R enumerate(R)(R range, int start=0) if (isInputRange!R) {
return Enumerate!R(range, start);
}
void main() {
auto flags = [0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1];
flags.enumerate(2).filter!q{!a[1]}().map!q{a[0]}().writeln();
{
import std.conv;
auto a = ["a", "b", "c"];
foreach(i, ref item; a.enumerate())
item = to!string(i);
assert(a == ["0", "1", "2"]);
}
}
artur