www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - _indexed_ iteration using opApply or range

reply spir <denis.spir gmail.com> writes:
Hello,

Had a nice time figuring out how to let opApply allow index iteration like:
    foreach (i, x ; collection) {}
Finally managed to do it adding 'i' everywhere:

struct S1 {
    private int[] elements =3D [];
    int opApply (int delegate (ref uint, ref int) block) {
        foreach (uint i, int n ; this.elements)
            block(i, n);
        return 0;
    }
}

Is this the intended idiom?
Now, I'm trying to make this work with ranges instead (input range only as =
of now). Seems i'm not smart enough to guess it alone...

Thank you,
Denis
-- -- -- -- -- -- --
vit esse estrany =E2=98=A3

spir.wikidot.com
Dec 12 2010
next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
spir <denis.spir gmail.com> wrote:

 Hello,

 Had a nice time figuring out how to let opApply allow index iteration  
 like:
     foreach (i, x ; collection) {}
 Finally managed to do it adding 'i' everywhere:

 struct S1 {
     private int[] elements = [];
     int opApply (int delegate (ref uint, ref int) block) {
         foreach (uint i, int n ; this.elements)
             block(i, n);
         return 0;
     }
 }

 Is this the intended idiom?

opApply is a strange beast. Your version does not handle early returns or other errors, for which block's return type should be checked: int opApply( int delegate( ref uint, ref int ) block ) { foreach ( uint i, int n; this.elements ) { if ( auto ret = block( i, n ) != 0 ) { return ret; } } return 0; } As for the i, yes, you have to do it like that. In fact, you can have as many parameters you want for the delegate, and refer to them in the foreach: struct foo{ int opApply( T... )( int delegate( ref T ) dg ) { return 0; } } foo f; foreach ( int i, double d, string s; f ) {}
 Now, I'm trying to make this work with ranges instead (input range only  
 as of now). Seems i'm not smart enough to guess it alone...

The trick to ranges is that they modify themselves. For a simple array wrapper range this may be a way: struct wrapper( T ) { T[] data; void popFront( ) { data = data[1..$]; } ref T front( ) { return data[0]; } bool empty( ) { return data.length == 0; } } Feel free to ask if you wonder about anything specific. -- Simen
Dec 12 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 12 December 2010 12:15:00 Simen kjaeraas wrote:
 spir <denis.spir gmail.com> wrote:
 Hello,
 
 Had a nice time figuring out how to let opApply allow index iteration
 
 like:
     foreach (i, x ; collection) {}
 
 Finally managed to do it adding 'i' everywhere:
 
 struct S1 {
 
     private int[] elements = [];
     int opApply (int delegate (ref uint, ref int) block) {
     
         foreach (uint i, int n ; this.elements)
         
             block(i, n);
         
         return 0;
     
     }
 
 }
 
 Is this the intended idiom?

opApply is a strange beast. Your version does not handle early returns or other errors, for which block's return type should be checked: int opApply( int delegate( ref uint, ref int ) block ) { foreach ( uint i, int n; this.elements ) { if ( auto ret = block( i, n ) != 0 ) { return ret; } } return 0; } As for the i, yes, you have to do it like that. In fact, you can have as many parameters you want for the delegate, and refer to them in the foreach: struct foo{ int opApply( T... )( int delegate( ref T ) dg ) { return 0; } } foo f; foreach ( int i, double d, string s; f ) {}
 Now, I'm trying to make this work with ranges instead (input range only
 as of now). Seems i'm not smart enough to guess it alone...

The trick to ranges is that they modify themselves. For a simple array wrapper range this may be a way: struct wrapper( T ) { T[] data; void popFront( ) { data = data[1..$]; } ref T front( ) { return data[0]; } bool empty( ) { return data.length == 0; } } Feel free to ask if you wonder about anything specific.

I'd also point out that the correct type for indexing is generally size_t. That's what arrays use. I believe that size_t _is_ uint on 32 bit machines, but it's going to be ulong on 64 bit machines. So, using uint is not portable. - Jonathan M Davis
Dec 12 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 12 Dec 2010 21:15:00 +0100
"Simen kjaeraas" <simen.kjaras gmail.com> wrote:

 The trick to ranges is that they modify themselves. For a simple array =20
 wrapper
 range this may be a way:
=20
 struct wrapper( T ) {
      T[] data;
      void popFront( ) {
          data =3D data[1..$];
      }
      ref T front( ) {
          return data[0];
      }
      bool empty( ) {
          return data.length =3D=3D 0;
      }
 }
=20
 Feel free to ask if you wonder about anything specific.

Thank you, Simen. 2 comments: (1) In my case, I do not want to modify the range (actually the private col= lection), because the type is not only about beeing a range (~ provide iter= ation). So that I defined the range methods, using a private 'rangeIndex sl= ot', as (the type also maintains a 'length' slot): private uint rangeIndex; property void popFront () { // (re)start... if (this.rangeIndex >=3D this.length) this.rangeIndex =3D 0; // ...or move on else ++ this.rangeIndex; } property bool empty () { return (this.rangeIndex >=3D this.length); } property T front () { return this.stacks[this.rangeIndex]; } Note that this also enables a restarting feature :-) (2) How then can I introduce indexed iteration (or more generally iteration= with more than one parameter to the block), using ranges? The blocking (!)= point is there is no way for front() to return a several multiple results. I can cheat using a tuple or struct, but D won't understand and unpack it f= or the user :-) Sure, the user may know I return a pack, but... Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 12 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
spir <denis.spir gmail.com> wrote:

 (1) In my case, I do not want to modify the range (actually the private  
 collection), because the type is not only about beeing a range (~  
 provide iteration). So that I defined the range methods, using a private  
 'rangeIndex slot', as (the type also maintains a 'length' slot):

     private uint rangeIndex;
      property void popFront () {
         // (re)start...
         if (this.rangeIndex >= this.length)
             this.rangeIndex = 0;
         // ...or move on
         else
             ++ this.rangeIndex;
     }
      property bool empty () {
         return (this.rangeIndex >= this.length);
     }
      property T front () {
         return this.stacks[this.rangeIndex];
     }

 Note that this also enables a restarting feature :-)

In this case, you probably want to use the idiom I described in the other thread, that opSlice() return a range struct. Consider: foreach ( i, e; myRange ) { if ( i == 3 ) break; } foreach ( i, e; myRange ) { // What is i? } For the restarting feature, forward ranges expose a function save(), which returns a copy of the range as it was at the time of save() being called. In many cases, save's body is simply 'return this;'.
 (2) How then can I introduce indexed iteration (or more generally  
 iteration with more than one parameter to the block), using ranges? The  
 blocking (!) point is there is no way for front() to return a several  
 multiple results.
 I can cheat using a tuple or struct, but D won't understand and unpack  
 it for the user :-) Sure, the user may know I return a pack, but...

I know. There is currently no solution to this except opApply. -- Simen
Dec 12 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 12 Dec 2010 14:47:04 -0500, spir <denis.spir gmail.com> wrote:

 Hello,

 Had a nice time figuring out how to let opApply allow index iteration  
 like:
     foreach (i, x ; collection) {}
 Finally managed to do it adding 'i' everywhere:

 struct S1 {
     private int[] elements = [];
     int opApply (int delegate (ref uint, ref int) block) {
         foreach (uint i, int n ; this.elements)
             block(i, n);
         return 0;
     }
 }

 Is this the intended idiom?
 Now, I'm trying to make this work with ranges instead (input range only  
 as of now). Seems i'm not smart enough to guess it alone...

It's not possible. There is no mechanism for foreach to use an 'index' field from the range. What's wrong with using opApply? You should be able to define both range primitives and opApply and opApply will be used when foreach is used, and the range primitives will be used by things like std.algorithm. -Steve
Dec 13 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Mon, 13 Dec 2010 09:47:39 -0500
"Steven Schveighoffer" <schveiguy yahoo.com> wrote:

 What's wrong with using opApply?  You should be able to define both range=

 primitives and opApply and opApply will be used when foreach is used, and=

 the range primitives will be used by things like std.algorithm.

Hope opApply would take precedence for iteration. I like range interface fo= r its integration with other language features, like funcs from std.algorit= hm precisely. May end up using both (if works properly -- i have other issu= es with ranges). Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 13 2010