www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - One way to look at foreach as not so bad

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
One of the things I've said recently I find distasteful about foreach 
(not even foreach_reverse, just plain foreach) is that in the statement 
foreach(i; &obj.foreach){}  'foreach' doesn't really 'foreach' anything. 
  The method obj.foreach is the one that really contains the "for each" 
loop, and obj.foreach is the one that really decides if it will even be 
"for each item" or only "for some items".

In short 'foreach' only means 'foreach' if you use it in a particular way.

It occurred to me, though, that a similar argument applies to good old 
'for'.

   for(i=0; i<3; i++) {}

C's 'for' is really totally cryptic if you think about it.  And can do 
just about anything you want it to, including things having nothing to 
do with 'for'-ing.
   for(do_laundry();  wash_car() ; clean_room()) {
      take_out_the_trash();
   }
That's legal, but whatever it's doing sure is not 'for'-ing anything.

I kind of stopped seeing that years ago though.  Thinking back, however, 
I can remember the very early days staring at the first few 'for' 
statements I ever saw in K&R, trying to figure out what the heck all 
those semicolons were doing, and why good old "for i=1 to 10" wasn't 
good enough, and later flipping back and forth in K&R to figure out 
for-loop code later in the book.

foreach is pretty similar.  It may have many uses that have nothing to 
do with 'foreach'-ing, and the meaning of the semicolon is meaningless 
and unclear at first, but I'll probably get used to it.

That doesn't mean it's an optimal design, though. ;-P

Nor does it mean that I wouldn't like trailing delegates to work also:
     obj.each() (int i) {
       writefln(i);
     }

Maybe I can get used to this version that works now:
     obj.each( (int i) {
       wrietfln(i);
     })

But it just looks weird to have my loop body inside the parens, and will 
look even weirder if nested
     obj.each( (Obj c) {
       c.each( (int i) {
         writefln(i);
       })
     })

It sort of looks like nested loops, but I just can't get excited about 
all those })'s screaming out "I'm a not a loop! I'm a function call!".

All that's just about plain 'foreach'.  I still can't see a good 
justification for foreach_reverse.  Reverse iteration just isn't common 
enough to justify a special case like that.

--bb
Oct 19 2006
next sibling parent reply Ary Manzana <asterite gmail.com> writes:
 All that's just about plain 'foreach'.  I still can't see a good 
 justification for foreach_reverse.  Reverse iteration just isn't common 
 enough to justify a special case like that.

I finally think Walter is right with foreach_reverse. It will be used mostly with arrays, and the compiler can then optimize the loop. Read this: http://www.digitalmars.com/d/faq.html#foreach Replace the article with: --- When should I use a foreach_reverse loop rather than a for? By using foreach_reverse, you are letting the compiler decide on the optimization rather than worrying about it yourself. Is it just performance or readability? --- :) Maybe the compiler can't optimize that much. But D is a system's programming language, and then it should do as much effort as possible to optimize the final code. Ary
Oct 20 2006
parent Bill Baxter <wbaxter gmail.com> writes:
Ary Manzana wrote:
 All that's just about plain 'foreach'.  I still can't see a good 
 justification for foreach_reverse.  Reverse iteration just isn't 
 common enough to justify a special case like that.

I finally think Walter is right with foreach_reverse. It will be used mostly with arrays, and the compiler can then optimize the loop. Read this: http://www.digitalmars.com/d/faq.html#foreach Replace the article with: --- When should I use a foreach_reverse loop rather than a for? By using foreach_reverse, you are letting the compiler decide on the optimization rather than worrying about it yourself. Is it just performance or readability? --- :) Maybe the compiler can't optimize that much. But D is a system's programming language, and then it should do as much effort as possible to optimize the final code.

Hmm. From that page:
 Like register assignment, let the compiler do the optimization.


That's great, but adding foreach_reverse is basically a 'register'-like hint to the compiler that isn't smart enough to figure out how to optimize this: foreach(i; &foo.reverse) That's fine, though, the 'register' keyword was also useful as a way to micro-optimize C code in the old days. Just kinda goes against the D philosophy of the "trust the compiler; the compiler is smarter than you". --bb
Oct 20 2006
prev sibling next sibling parent reply BCS <BCS pathlink.com> writes:
Bill Baxter wrote:

 Maybe I can get used to this version that works now:
     obj.each( (int i) {
       wrietfln(i);
     })
 
 But it just looks weird to have my loop body inside the parens, and will 
 look even weirder if nested
     obj.each( (Obj c) {
       c.each( (int i) {
         writefln(i);
       })
     })
 
 It sort of looks like nested loops, but I just can't get excited about 
 all those })'s screaming out "I'm a not a loop! I'm a function call!".

I'm not to happy about foreach_reverse (more neutral than anything) but I do see a major advantage of both foreaches over the form mentioned above: outer: foreach(Obj c; &obj.each) foreach(int i; &c.each) { break outer; continue outer; break; continue; return; } any of the lines in the inner loop quickly become an ugly hack when the user is writing the blocks as delegates.
 
 All that's just about plain 'foreach'.  I still can't see a good 
 justification for foreach_reverse.  Reverse iteration just isn't common 
 enough to justify a special case like that.
 

A templateized reverse iterator would be good enough for me. int delegate(int delegate(T)) reverse(T)(T[]){...} foreach(int i; reverse([1,2,3,4,5,6,7,8,9])) { }
 --bb

Oct 20 2006
parent reply Bill Baxter <wbaxter gmail.com> writes:
BCS wrote:
 Bill Baxter wrote:
 
 I'm not to happy about foreach_reverse (more neutral than anything) but 
 I do see a major advantage of both foreaches over the form mentioned above:
 
 outer: foreach(Obj c; &obj.each)
    foreach(int i; &c.each)
    {
       break outer;
       continue outer;
       break;
       continue;
       return;
    }

True. But I think I've needed to break out of inner loops like that maybe 5 times in 20 years of coding. YMMV.
 any of the lines in the inner loop quickly become an ugly hack when the 
 user is writing the blocks as delegates.

--bb
Oct 20 2006
parent BCS <BCS pathlink.com> writes:
Bill Baxter wrote:
 
 
 True.  But I think I've needed to break out of inner loops like that 
 maybe 5 times in 20 years of coding.  YMMV.
 
 

It has varied quite a bit actually, but maybe that says something (good, bad or otherwise) about my codeing style.
 
 --bb

Oct 20 2006
prev sibling parent reply =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= writes:
Bill Baxter wrote:
 Maybe I can get used to this version that works now:
     obj.each( (int i) {
       wrietfln(i);
     })
 
 But it just looks weird to have my loop body inside the parens, and will 
 look even weirder if nested
     obj.each( (Obj c) {
       c.each( (int i) {
         writefln(i);
       })
     })

I don't understand your point. You can already write something like: obj.each = (int i) { writefln(i); };
Oct 20 2006
next sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Julio CÚsar Carrascal Urquijo wrote:
 Bill Baxter wrote:
 Maybe I can get used to this version that works now:
     obj.each( (int i) {
       wrietfln(i);
     })

 But it just looks weird to have my loop body inside the parens, and 
 will look even weirder if nested
     obj.each( (Obj c) {
       c.each( (int i) {
         writefln(i);
       })
     })

I don't understand your point. You can already write something like: obj.each = (int i) { writefln(i); };

Yup, or obj.each in (int i) { writefln(i); }; But it will only look like a loop of some sort. Neither continue, break nor return will work in its body. The trailing delegate extension would make life easier *hint* *hint* ;)
Oct 20 2006
parent reply =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= writes:
Tom S wrote:
  > Yup, or
 
 obj.each in (int i) {
     writefln(i);
 };

Well yes, "in" looks better but it doesn't work right now, "=" does.
 But it will only look like a loop of some sort. Neither continue, break 
 nor return will work in its body.
 The trailing delegate extension would make life easier *hint* *hint* ;)

I see, that would certainly require compiler support, like what the compiler is doing for the foreach statement (Returning an int to flag break, continue, return) or it will be forcing the user to write the return statements all the time.
Oct 20 2006
parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Julio CÚsar Carrascal Urquijo wrote:
 Tom S wrote:
  > Yup, or
 obj.each in (int i) {
     writefln(i);
 };

Well yes, "in" looks better but it doesn't work right now, "=" does.

Sure it works ! :) import std.stdio; class Foo { int[] data; struct Iter_each { Foo owner; void opIn(void delegate(int) dg) { foreach (int d; owner.data) { dg(d); } } } Iter_each each() { Iter_each res; res.owner = this; return res; } } void main() { Foo foo = new Foo; foo.data = [34, 2, 55, 23]; foo.each in (int i) { writefln(i); }; } -- Tomasz Stachowiak
Oct 21 2006
prev sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
Julio CÚsar Carrascal Urquijo wrote:
 Bill Baxter wrote:
 
 Maybe I can get used to this version that works now:
     obj.each( (int i) {
       wrietfln(i);
     })

 But it just looks weird to have my loop body inside the parens, and 
 will look even weirder if nested
     obj.each( (Obj c) {
       c.each( (int i) {
         writefln(i);
       })
     })

I don't understand your point. You can already write something like: obj.each = (int i) { writefln(i); };

How do you set that up? Something like this? class ObjClass { class EachClass { void opAssign( void delegate(...) ) { [...] } } EachClass each = new EachClass(); } ObjClass obj = new ObjClass(); Or is there a better way? --bb
Oct 20 2006
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bill Baxter wrote:
 Julio CÚsar Carrascal Urquijo wrote:
 I don't understand your point. You can already write something like:

 obj.each = (int i) {
     writefln(i);
 };

How do you set that up? Something like this? class ObjClass { class EachClass { void opAssign( void delegate(...) ) { [...] } } EachClass each = new EachClass(); } ObjClass obj = new ObjClass();

That won't work. There is no opAssign.
 Or is there a better way?

In the sense that it works, yes. Something like this: class ObjClass { void each(void delegate(...) ) { [...] } } ObjClass obj = new ObjClass(); This somewhat abuses the property syntax, but should result in the syntax above.
Oct 21 2006
parent Bill Baxter <wbaxter gmail.com> writes:
Frits van Bommel wrote:
 Bill Baxter wrote:
 
 Julio CÚsar Carrascal Urquijo wrote:

 I don't understand your point. You can already write something like:

 obj.each = (int i) {
     writefln(i);
 };

How do you set that up? Something like this? class ObjClass { class EachClass { void opAssign( void delegate(...) ) { [...] } } EachClass each = new EachClass(); } ObjClass obj = new ObjClass();

That won't work. There is no opAssign.

Oh yeh, that's right. :-) I think "opAssign" must have looked familar to me merely because of previous discussions about its lack. --bb
Oct 22 2006