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

Reply via email to