digitalmars.D - foreach without front
- Jonathan Marler (42/42) Aug 11 2014 I know this is kinda "nit picky" but it would be nice if foreach
- Idan Arye (25/67) Aug 11 2014 `foreach` should manage it's own iterator's resources - it
- Jonathan Marler (18/42) Aug 11 2014 You say `foreach` should manage it's own iterator's resources but
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (42/49) Aug 14 2014 The syntax does not allow that but I have discovered a WAT! :) If you
- Timon Gehr (31/45) Aug 14 2014 Why would this be surprising?
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (4/52) Aug 14 2014 Makes sense.
- H. S. Teoh via Digitalmars-d (43/91) Aug 14 2014 [...]
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
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
`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 canYou 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
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
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
On 08/14/2014 11:16 AM, Timon Gehr wrote:On 08/14/2014 07:39 PM, Ali Çehreli wrote:Because I was taking WAT as a type name. :-/ (jet-lagged here :p)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.Makes sense. Ali
Aug 14 2014
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