www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - foreach without front

reply "Jonathan Marler" <johnnymarler gmail.com> writes:
I know this is kinda "nit picky" but it would be nice if foreach 
supported iterating through input ranges without accessing the 
front function.

foreach(myInputRange) {
     // myInputRange has a front function but it is
     // never called because the foreach has no type list
}

One case where I think this would be preferable is when input 
ranges iterate over larger data structures or data that is 
trashed after every iteration.  Like the following example.

struct MyData {
   int someInt;
   string someString;
   ubyte[128] data;
}

struct MyDataInputRange {
   MyData* dataBuffer;
   this(MyData* dataBuffer) {
     this.dataBuffer = dataBuffer;
   }
    property bool empty() { /* empty logic */ }
    property MyData* front() { return dataBuffer; }
    property popFront() { }
}
void main()
{
   MyData data;
   foreach(dataPointer; MyDataInputRange(&data)) {
     // It doesn't make much sense to use dataPointer when you
     // already have direct access to the data buffer
   }
   foreach(MyDataInputRange(&data)) {
     // This allows you to iterate over the range using the same 
buffer
   }
}

I realize that in this case it results in such an infinitesimal 
optimization but I'm bringing this up because it seems like a 
feature that:
   1. would be relatively easy to implement
   2. could be useful in some other cases

Has anyone wanted this feature before?
Aug 11 2014
next sibling parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Monday, 11 August 2014 at 15:40:18 UTC, Jonathan Marler wrote:
 I know this is kinda "nit picky" but it would be nice if 
 foreach supported iterating through input ranges without 
 accessing the front function.

 foreach(myInputRange) {
     // myInputRange has a front function but it is
     // never called because the foreach has no type list
 }

 One case where I think this would be preferable is when input 
 ranges iterate over larger data structures or data that is 
 trashed after every iteration.  Like the following example.

 struct MyData {
   int someInt;
   string someString;
   ubyte[128] data;
 }

 struct MyDataInputRange {
   MyData* dataBuffer;
   this(MyData* dataBuffer) {
     this.dataBuffer = dataBuffer;
   }
    property bool empty() { /* empty logic */ }
    property MyData* front() { return dataBuffer; }
    property popFront() { }
 }
 void main()
 {
   MyData data;
   foreach(dataPointer; MyDataInputRange(&data)) {
     // It doesn't make much sense to use dataPointer when you
     // already have direct access to the data buffer
   }
   foreach(MyDataInputRange(&data)) {
     // This allows you to iterate over the range using the same 
 buffer
   }
 }

 I realize that in this case it results in such an infinitesimal 
 optimization but I'm bringing this up because it seems like a 
 feature that:
   1. would be relatively easy to implement
   2. could be useful in some other cases

 Has anyone wanted this feature before?
`foreach` should manage it's own iterator's resources - it shouldn't rely on some memory declared outside it's scope that'll be accessible after the loop is finished. You can always manage the iteration manually: struct MyData { int someInt; string someString; ubyte[128] data; } struct MyDataInputRange { MyData* dataBuffer; this(MyData* dataBuffer) { this.dataBuffer = dataBuffer; } bool moveNext() { /* return true unless reached the end of the loop */ } } void main() { MyData data; for(auto dataInputRange = MyDataInputRange(&data); dataInputRange.moveNext();) { } }
Aug 11 2014
parent "Jonathan Marler" <johnnymarler gmail.com> writes:
 `foreach` should manage it's own iterator's resources - it 
 shouldn't rely on some memory declared outside it's scope 
 that'll be accessible after the loop is finished. You can
You say `foreach` should manage it's own iterator's resources but why? The std.stdio function byChunk allows you to pass in a slice to a buffer to memory that is managed outside the iterators resources. Passing in memory-pointers to iterators is necessary for memory optimization in some cases.
 always manage the iteration manually:

     struct MyData {
         int someInt;
         string someString;
         ubyte[128] data;
     }

     struct MyDataInputRange {
         MyData* dataBuffer;
         this(MyData* dataBuffer) {
             this.dataBuffer = dataBuffer;
         }
         bool moveNext() { /* return true unless reached the end 
 of the loop */ }
     }
     void main()
     {
         MyData data;
         for(auto dataInputRange = MyDataInputRange(&data);
                 dataInputRange.moveNext();) {
         }
     }
Yes managing the array manually is an obvious solution but like I said I'm just being "nit picky". I am exposing this class in a library and would like to encourage others to use it in the optimal way which would be to omit the call to the front function and just access the struct directly. I also thought of another case where this would be useful: foreach(0..count) { // do something count number of times where // you don't need the current count } Again I don't see this being needed very often it would just be nice syntax sugar that I don't anticipate would be very hard to implement:)
Aug 11 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/11/2014 08:40 AM, Jonathan Marler wrote:

 I know this is kinda "nit picky" but it would be nice if foreach
 supported iterating through input ranges without accessing the front
 function.

 foreach(myInputRange) {
      // myInputRange has a front function but it is
      // never called because the foreach has no type list
 }
The syntax does not allow that but I have discovered a WAT! :) If you implement iteration by opApply() member functions, it is possible to use any random name and it works. The following type provides both an int and a void overload. import std.stdio; struct S { int opApply(int delegate(int) dg) { foreach (i; 0 .. 3) { int b = dg(i); if (b) { return b; } } return 0; } int opApply(int delegate() dg) { foreach (_; 0 .. 3) { int b = dg(); if (b) { return b; } } return 0; } } void main() { auto s = S(); foreach (i; s) { writeln(i); } // Replace WAT with any other random name and it still works. // foreach (_; s) would make the most sense. foreach (WAT; s) { writeln("_"); } } Ali
Aug 14 2014
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/14/2014 07:39 PM, Ali Çehreli wrote:
 On 08/11/2014 08:40 AM, Jonathan Marler wrote:

  > I know this is kinda "nit picky" but it would be nice if foreach
  > supported iterating through input ranges without accessing the front
  > function.
  >
  > foreach(myInputRange) {
  >      // myInputRange has a front function but it is
  >      // never called because the foreach has no type list
  > }

 The syntax does not allow that but I have discovered a WAT! :) If you
 implement iteration by opApply() member functions, it is possible to use
 any random name and it works.

 The following type provides both an int and a void overload.
 ...
Why would this be surprising? import std.stdio; struct S{ int opApply(int delegate(int) dg){ foreach(i;0..3) if(int b=dg(i)) return b; return 0; } int opApply(int delegate() dg){ foreach(i;0..3) if(int b=dg()) return b; return 0; } } void main(){ auto s = S(); foreach(i; s) writeln(i); foreach(WAT; s) writeln("_"); // the above is rewritten roughly to the following, // which is valid code, calling the first opApply overload switch(s.opApply((WAT){ writeln("_"); return 0; })){ default: break; } } This still works if the second opApply overload is removed. The current 'foreach' will never call it.
Aug 14 2014
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 08/14/2014 11:16 AM, Timon Gehr wrote:

 On 08/14/2014 07:39 PM, Ali Çehreli wrote:
 On 08/11/2014 08:40 AM, Jonathan Marler wrote:

  > I know this is kinda "nit picky" but it would be nice if foreach
  > supported iterating through input ranges without accessing the front
  > function.
  >
  > foreach(myInputRange) {
  >      // myInputRange has a front function but it is
  >      // never called because the foreach has no type list
  > }

 The syntax does not allow that but I have discovered a WAT! :) If you
 implement iteration by opApply() member functions, it is possible to use
 any random name and it works.

 The following type provides both an int and a void overload.
 ...
Why would this be surprising?
Because I was taking WAT as a type name. :-/ (jet-lagged here :p)
 import std.stdio;
 struct S{
      int opApply(int delegate(int) dg){
          foreach(i;0..3)
              if(int b=dg(i))
                  return b;
          return 0;
      }
      int opApply(int delegate() dg){
          foreach(i;0..3)
              if(int b=dg())
                  return b;
          return 0;
      }
 }

 void main(){
      auto s = S();
      foreach(i; s) writeln(i);
      foreach(WAT; s) writeln("_");
      // the above is rewritten roughly to the following,
      // which is valid code, calling the first opApply overload
      switch(s.opApply((WAT){
          writeln("_"); return 0;
      })){
          default: break;
      }
 }

 This still works if the second opApply overload is removed. The current
 'foreach' will never call it.
Makes sense. Ali
Aug 14 2014
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Aug 14, 2014 at 10:39:48AM -0700, Ali Çehreli via Digitalmars-d wrote:
[...]
 The syntax does not allow that but I have discovered a WAT! :) If you
 implement iteration by opApply() member functions, it is possible to
 use any random name and it works.
 
 The following type provides both an int and a void overload.
 
 import std.stdio;
 
 struct S
 {
     int opApply(int delegate(int) dg)
     {
         foreach (i; 0 .. 3) {
             int b = dg(i);
             if (b) {
                 return b;
             }
         }
 
         return 0;
     }
 
     int opApply(int delegate() dg)
     {
         foreach (_; 0 .. 3) {
             int b = dg();
             if (b) {
                 return b;
             }
         }
 
         return 0;
     }
 }
 
 void main()
 {
     auto s = S();
     foreach (i; s) {
         writeln(i);
     }
 
     // Replace WAT with any other random name and it still works.
     // foreach (_; s) would make the most sense.
     foreach (WAT; s) {
         writeln("_");
     }
 }
[...] This is not a WAT. The above code only calls the first overload of opApply. It's not a WAT to allow any random name -- since it's up to the user to choose the name of the iteration variable. If you print out the type and value of 'WAT', you'll see that it takes on the values passed in by the first overload of opApply (to disambiguate this from the built-in indexing, change the foreach loop in the first opApply to, say, 17..21, and look at the values of 'WAT'). It's actually not possible to call the second overload of opApply from within a foreach; I tried this: struct S { int opApply(scope int delegate() dg) { foreach (_; 0..3) { auto rc = dg(); if (rc) return rc; } return 0; } } void main() { S s; foreach (_; s) { } } and the compiler said: test.d(12): Error: cannot infer argument types, expected 0 argument, not 1 But if I delete the loop variable '_' (i.e., foreach (; s)), the compiler says: test.d(12): Error: basic type expected, not ; test.d(12): Error: no identifier for declarator int Deleting the ';' (i.e., foreach (s)) leads to: test.d(12): Error: no identifier for declarator s test.d(12): Error: found ')' when expecting ';' test.d(14): Error: found '}' when expecting ')' test.d(15): Error: found 'EOF' instead of statement test.d(15): Error: found 'EOF' when expecting '}' following compound statement So basically, it's impossible to invoke an opApply that takes an argumentless delegate from a foreach. T -- My program has no bugs! Only undocumented features...
Aug 14 2014