February 24, 2005
"Andy" <andy.knowles@gmail.com> wrote in message news:cvjr4s$2kj$1@digitaldaemon.com...
>
> Wouldn't this serve for now at least?  Personally I think it is better
> than
> adding new keywords/operators to the language...
>
> <code>
> module andy.backwards.backwards;
>
> import std.stdio;
>
> template backwards(List, Delegate)
> {
> struct reverse_iterator
> {
> List list;
>
> int opApply(Delegate dg)
> {
> return list.opApplyReverse(dg);
> }
> }
>
> reverse_iterator backwards()
> {
> reverse_iterator r;
> r.list = this;
> return r;
> }
> }
>
> class Foo
> {
> mixin backwards!(Foo, int delegate(inout int));
>
> int opApply(int delegate(inout int) dg)
> {
> int result = 0;
> for(int i = 0; i < 10; i++)
> {
> result = dg(i);
> if(result)
> break;
> }
> return result;
> }
>
> int opApplyReverse(int delegate(inout int) dg)
> {
> int result = 0;
> for(int i = 9; i >= 0; i--)
> {
> result = dg(i);
> if(result)
> break;
> }
> return result;
> }
> }
>
> void main(char[][] args)
> {
> Foo foo = new Foo();
>
> foreach(int i; foo)
> writefln(i);
>
> foreach(int i; foo.backwards)
> writefln(i);
> }
> </code>
>
> Adding a reverse iterator to a class requires only a mixin and the
> definition of
> the iterator itself (called opApplyReverse here).  Calling the iterator
> requires
> only a .backwards on the end of the foreach expression.
>
> You can replace the templated delegate type with a templated item type to
> make
> typing the mixins easier, but then you can't create different iterators
> (say
> with an accompanying index).
>
> Hopefully with inlining and optomising this would be about as fast as
> normal
> foreaching?

That's one way. Here's what I did in MinTL for backwards iteration over arrays. Actually the code in MinTL also can produce a "sequence" (which is a run-time list of items) from an array but I removed that part since it doesn't matter here. One decision I remember wondering about was the count in foreach statements - should it just count 0 to n-1 or should it count n-1 to 0? The code I have here counts 0 to n-1 but I'm thinking now maybe it should count n-1 to 0.

/** Iterate backwards over a dynamic array. This function should be
 *  used on the target array in a foreach statement
 *  \param x the array to iterate over.
 */
template backwards(Value : Value[]) {
  DArrayReverseIter!(Value) backwards(Value[] x) {
    DArrayReverseIter!(Value) y;
    y.x = x;
    return y;
  }
}
/* Private helper for reverse iteration */
private struct DArrayReverseIter(Value) {
  Value[] x;
  int opApply(int delegate(inout Value val) dg) {
    int res = 0;
    for (int n=x.length; n > 0; ) {
      res = dg(x[--n]);
      if (res) break;
    }
    return res;
  }
  int opApply(int delegate(inout int n, inout Value val) dg) {
    int res = 0;
    int cnt = 0;
    for (int n=x.length; n > 0; cnt++) {
      res = dg(cnt,x[--n]);
      if (res) break;
    }
    return res;
  }
}

import std.stdio;
int main() {
    int[] t1, t2;
    t1.length = 4;
    t2.length = 4;
    for(int k=0;k<4;k++) t1[k] = k*100;
    foreach(int n, int val; backwards!(int[])(t1)) {
      writefln("n=%d val=%d",n,val);
      t2[n] = val;
    }
    assert( t1.reverse == t2 );
    return 0;
}


February 24, 2005
> You can't foreach on an AA by itself, you have to foreach on the .keys or
> the
> values properties of the AA, which are just regular arrays.

yes, you can. for example:
import std.stdio;
int main() {
  int[int] x;
  x[100]=1;
  x[-100]=2;
  x[10]=3;
  foreach(int key, int val; x) {
    writefln("key %d val %d",key,val);
  }
  return 0;
}


> So if the foreach-reverse keyword is introduced, you'd reverse iterate on
> the
> keys or the values of the AA.
>
> Of course, reverse iteration on the keys or values doesn't matter much,
> unless
> you want to reverse iterate through the SORTED keys or values, which DOES
> make
> sense. :)
>
> Regards,
> James Dunne


February 24, 2005
>>>> I think the options are:
>>>> - add the ability to specify the 'step' in foreach
[snip]
> Not necessarily.  Foreach was created to iterate through lists.  for() was
> meant
> to give flexible looping ability.  If you want to skip indicies, use a
> regular
> for() loop.  Any well-implemented class that implements opApply (foreach
> support) should also implement opIndex to give access to individual
> elements.

Though [] indexing can be very slow in the middle of a linked list.

> Besides, adding a step, start, and end to the current foreach syntax has
> the
> possibility of breaking code as it stands now.  However, they could be
> optional
> arguments and not break anything, or they could be an optionally allowed
> syntax
> in addition to the "old (current) way".

Start and stop is not needed since that is what slicing is for. Stepping
(I'm including reverse as the special case step = -1) is the only thing that
isn't possible today without a "for" loop. This might have been proposed
before- I can't remember. I think the following syntax is backwards
compatible:
  foreach (ForeachTypeList; Expression [;Expression]) Statement
where the optional second expression is the step. If the step is specified
the opApply signature must match
  int opApply(int delegate(inout Type [, ...]) dg, int step);
If there isn't an opApply with a step parameter it is an error. If the step
is negative the traversal is backwards.

Using a negative step as reverse means it is potentially a run-time
decision. Also it isn't as explicit as having the word "reverse" or
"backwards" or "_r" in there somewhere. But it would be nice to kill a few
birds with one stone and it closely resembles how a for loop looks:
for(start; stop; step) {...}
foreach(iterator; container; step) {...}



February 24, 2005
On Thu, 24 Feb 2005 05:34:44 +0000 (UTC), James Dunne <jdunne4@bradley.edu> wrote:
> In article <opsmooadap23k2f5@ally>, Regan Heath says...
>>
>> On Thu, 24 Feb 2005 03:41:02 +0000 (UTC), James Dunne
>> <jdunne4@bradley.edu> wrote:
>>> In article <opsmol1osx23k2f5@ally>, Regan Heath says...
>>>>
>>>> Does the term "foreach" imply any direction forward or backward? To me
>>>> it
>>>> doesn't. I realise it's used in other languages, the same as in D, so it
>>>> has a commonly understood meaning.
>>>>
>>>> I think the options are:
>>>>
>>>> - leave foreach, invent a backwards term i.e. foreach_r.
>>>> - invent new terms, one for forward, one for backward.
>>>> - add the ability to specify the 'step' in foreach
>>>>
>>>>
>>>> Regan
>>>
>>> I agree with the options listed, and really they're not up to me to
>>> decide ;).
>>> On specifying the 'step' for the foreach, that doesn't seem especially
>>> useful
>>> unless it's either +1 or -1, which I'm sure is what your point was.
>>
>> Actually, I was being intentionally vague :)
>>
>>> I'm just
>>> saying I don't see why anyone would use a container to foreach through
>>> on a
>>> specified interval bigger than one.
>>
>> Isn't it the same reason someone would use a for loop with i+=2, or i-=2?
>>
>
> Not necessarily.  Foreach was created to iterate through lists.  for() was meant
> to give flexible looping ability.  If you want to skip indicies, use a regular
> for() loop. Any well-implemented class that implements opApply (foreach
> support) should also implement opIndex to give access to individual elements.

I believe the other important goal was to make iteration through a container look identical, regardless of the container type. To save re-coding when changing container type, or writing generic code.

If foreach can be modified to allow accessing every xth item of any container with the same syntax, regardless of type i.e. array, linklist, then I think this is the best solution.

Given that opIndex for a linklist is inefficient, not having foreach do it, will mean people will use a for loop and be in the same situation as before foreach existed, recoding and having trouble writing generic code.

> Besides, adding a step, start, and end to the current foreach syntax has the
> possibility of breaking code as it stands now.

I think breaking code pre 1.0 is ok, case by case, if we want to we'll have to consider it.

> However, they could be optional
> arguments and not break anything, or they could be an optionally allowed syntax
> in addition to the "old (current) way".

This would be best if possible. Ben has come up with an idea I see.

>> Of course in addition to the step you need some way to indicate where to
>> start start, end, x.
>>
>> That is, unless you assume -1 means start at the end, and +1 means start
>> at the beginning and that no-one ever wants to start elsewhere.
>>
>> I think the concept of direction and step can apply to arrays, linklists,
>> and other types of containers, if so does that mean foreach should handle
>> them?
>>
>
> If you can give me a legitimate example where you would *need* to skip elements
> on a constant step using foreach, then I'll agree with you.  I just can't think
> of any off-hand.

Do you want a concrete example? or are the comments above about the goal of foreach and linklists sufficient?

>>> Maybe a punny syntax like "foreach" is short for forward-each
>>
>> I thought it was "for each" as in "for each item in .."
>>
>
> That's technically what it means, yes.  But I was just throwing in the pun of
> foreach breaking up as "forward" and "each" to allow a backeach meaning "back"
> and "each".  Just a dumb pun, don't worry about it ;)

Ahh. Sorry, my sense of humour must have been on holiday.

>>> , and we could have
>>> "backeach".  foreach to me seems to imply order, and is usually
>>> implemented as a
>>> forward iteration.
>>
>> I suspected that to others it might imply order.
>>
>
> And it does.  When you foreach through a D array, you iterate forward through
> it.  It's just generally accepted that it has a forward order to it.  In other
> languages (e.g. PHP, Perl, C#) this is true as well.

Yep. Tho, I am one of those that doesn't have a problem being different, if I think different is better.

Regan
February 25, 2005
In article <cvkk7t$1194$1@digitaldaemon.com>, Ben Hinkle says...
>
>> You can't foreach on an AA by itself, you have to foreach on the .keys or
>> the
>> values properties of the AA, which are just regular arrays.
>
>yes, you can. for example:
>import std.stdio;
>int main() {
>  int[int] x;
>  x[100]=1;
>  x[-100]=2;
>  x[10]=3;
>  foreach(int key, int val; x) {
>    writefln("key %d val %d",key,val);
>  }
>  return 0;
>}
>

That's a new one to me!  It's not explained in the documentation, so I didn't think it existed.  Looks like I have to do some research on the foreach allowable syntax to see what it's really capable of.  This could just boil down to a simple documentation problem (or lack thereof).

Regards,
James Dunne
February 25, 2005
On Fri, 25 Feb 2005 00:58:20 +0000 (UTC), James Dunne <jdunne4@bradley.edu> wrote:
> In article <cvkk7t$1194$1@digitaldaemon.com>, Ben Hinkle says...
>>
>>> You can't foreach on an AA by itself, you have to foreach on the .keys or
>>> the
>>> values properties of the AA, which are just regular arrays.
>>
>> yes, you can. for example:
>> import std.stdio;
>> int main() {
>>  int[int] x;
>>  x[100]=1;
>>  x[-100]=2;
>>  x[10]=3;
>>  foreach(int key, int val; x) {
>>    writefln("key %d val %d",key,val);
>>  }
>>  return 0;
>> }
>>
>
> That's a new one to me!  It's not explained in the documentation, so I didn't
> think it existed.  Looks like I have to do some research on the foreach
> allowable syntax to see what it's really capable of.  This could just boil down
> to a simple documentation problem (or lack thereof).

IIRC the above doesn't give you 'sorted' information, so it's only really good if you don't care about what order it comes out in.

Regan
February 25, 2005
Perhaps a good all round extension to the foreach statement would be if it took delegates to iterators as well.  Then the following would work:

import std.stdio;

class Foo
{
  int opApply(int delegate(inout int) dg)
  {
    //default iterator
    return forwards(dg);
  }

  int forwards(int delegate(inout int) dg)
  {
    int result = 0;
    for(int i = 0; i < 10; i++)
    {
      result = dg(i);
      if(result)
        break;
    }
    return result;
  }

  int backwards(int delegate(inout int) dg)
  {
    int result = 0;
    for(int i = 9; i >= 0; i--)
    {
      result = dg(i);
      if(result)
        break;
    }
    return result;
  }
}

void main(char[][] args)
{
  Foo foo = new Foo();

  //implicit call to foo.opApply
  foreach(int i; foo)
    writefln(i);

  //explicit call to foo.opApply
  foreach(int i; &foo.opApply)
    writefln(i);

  //explicitly use forward iterator
  foreach(int i; &foo.forwards)
    writefln(i);

  //explicitly use reverse iterator
  foreach(int i; &foo.backwards)
    writefln(i);
}

The full type of the delegate would be int delegate(int delegate(inout
_type_ [, ...])).  I had a quick glance at the foreach conversion code in
statement.c, this change should be pretty simple.

This way you can have several named iterators on any class available for easy usage, say for different tree traversals.  You could pass around iterators as delegates, allowing you to write code that operates on an entire collection while being completely independant of the type of the collection itself.  If you really didn't like having the amperstand, you could write a public property to return a private delegate.  And best of all, the only thing you have changed is the type of expression a foreach accepts.  No new syntax, statements, operators, or types of expressions.

Sound good?

"Ben Hinkle" <ben.hinkle@gmail.com> wrote in message news:cvkjv1$110l$1@digitaldaemon.com...
>
> "Andy" <andy.knowles@gmail.com> wrote in message news:cvjr4s$2kj$1@digitaldaemon.com...
> >
> > Wouldn't this serve for now at least?  Personally I think it is better
> > than
> > adding new keywords/operators to the language...
> >
> > <code>
> > module andy.backwards.backwards;
> >
> > import std.stdio;
> >
> > template backwards(List, Delegate)
> > {
> > struct reverse_iterator
> > {
> > List list;
> >
> > int opApply(Delegate dg)
> > {
> > return list.opApplyReverse(dg);
> > }
> > }
> >
> > reverse_iterator backwards()
> > {
> > reverse_iterator r;
> > r.list = this;
> > return r;
> > }
> > }
> >
> > class Foo
> > {
> > mixin backwards!(Foo, int delegate(inout int));
> >
> > int opApply(int delegate(inout int) dg)
> > {
> > int result = 0;
> > for(int i = 0; i < 10; i++)
> > {
> > result = dg(i);
> > if(result)
> > break;
> > }
> > return result;
> > }
> >
> > int opApplyReverse(int delegate(inout int) dg)
> > {
> > int result = 0;
> > for(int i = 9; i >= 0; i--)
> > {
> > result = dg(i);
> > if(result)
> > break;
> > }
> > return result;
> > }
> > }
> >
> > void main(char[][] args)
> > {
> > Foo foo = new Foo();
> >
> > foreach(int i; foo)
> > writefln(i);
> >
> > foreach(int i; foo.backwards)
> > writefln(i);
> > }
> > </code>
> >
> > Adding a reverse iterator to a class requires only a mixin and the
> > definition of
> > the iterator itself (called opApplyReverse here).  Calling the iterator
> > requires
> > only a .backwards on the end of the foreach expression.
> >
> > You can replace the templated delegate type with a templated item type
to
> > make
> > typing the mixins easier, but then you can't create different iterators
> > (say
> > with an accompanying index).
> >
> > Hopefully with inlining and optomising this would be about as fast as
> > normal
> > foreaching?
>
> That's one way. Here's what I did in MinTL for backwards iteration over arrays. Actually the code in MinTL also can produce a "sequence" (which is
a
> run-time list of items) from an array but I removed that part since it doesn't matter here. One decision I remember wondering about was the count in foreach statements - should it just count 0 to n-1 or should it count
n-1
> to 0? The code I have here counts 0 to n-1 but I'm thinking now maybe it should count n-1 to 0.
>
> /** Iterate backwards over a dynamic array. This function should be
>  *  used on the target array in a foreach statement
>  *  \param x the array to iterate over.
>  */
> template backwards(Value : Value[]) {
>   DArrayReverseIter!(Value) backwards(Value[] x) {
>     DArrayReverseIter!(Value) y;
>     y.x = x;
>     return y;
>   }
> }
> /* Private helper for reverse iteration */
> private struct DArrayReverseIter(Value) {
>   Value[] x;
>   int opApply(int delegate(inout Value val) dg) {
>     int res = 0;
>     for (int n=x.length; n > 0; ) {
>       res = dg(x[--n]);
>       if (res) break;
>     }
>     return res;
>   }
>   int opApply(int delegate(inout int n, inout Value val) dg) {
>     int res = 0;
>     int cnt = 0;
>     for (int n=x.length; n > 0; cnt++) {
>       res = dg(cnt,x[--n]);
>       if (res) break;
>     }
>     return res;
>   }
> }
>
> import std.stdio;
> int main() {
>     int[] t1, t2;
>     t1.length = 4;
>     t2.length = 4;
>     for(int k=0;k<4;k++) t1[k] = k*100;
>     foreach(int n, int val; backwards!(int[])(t1)) {
>       writefln("n=%d val=%d",n,val);
>       t2[n] = val;
>     }
>     assert( t1.reverse == t2 );
>     return 0;
> }
>
>


February 25, 2005
"Ben Hinkle" <ben.hinkle@gmail.com> wrote:

[...]
> foreach(iterator; container; step) {...}

I am really stunned of this thread. I never heard of a construct in a meta language where "for each" is used without the meaning that whatever follows is to be interpreted as a set of items without any particular order. Am I out of bounds in this case? Can you show me an example where such thing is really used --- and of benefit? And please do not come with the example of a dictator who wants to shoot down every `step'th member of a town or elsewise defined group of people.

-manfred
February 25, 2005
"Manfred Nowak" <svv1999@hotmail.com> wrote in message news:cvn4bb$j49$1@digitaldaemon.com...
> "Ben Hinkle" <ben.hinkle@gmail.com> wrote:
>
> [...]
>> foreach(iterator; container; step) {...}
>
> I am really stunned of this thread. I never heard of a construct in a meta language where "for each" is used without the meaning that whatever follows is to be interpreted as a set of items without any particular order.

That's a legit view. I've always viewed foreach as another kind of for loop - one that handles tree traversals more cleanly than using STL-style iterators. The "each" in "foreach" indicates we go through a bunch of items, as you suggest, but I never got the impressions of any more semantics than that. I don't the see the usefulness of a step other than 1 or -1 for trees, though. but for lists of things it would be handy.

> Am I out of bounds in this case?

There is no spoon.

> Can you show me an
> example where such thing is really used --- and of benefit? And
> please do not come with the example of a dictator who wants to shoot
> down every `step'th member of a town or elsewise defined group of
> people.
>
> -manfred

I'll give an example from MATLAB. It is common to pass around string-value lists like ['Color', 'red', 'LineWidth', 2.4, 'LineStyle', ,'--'] and then have code that runs down the strings or values by stepping by 2.

An example closer to D would be having a list of points in a polygon and wanting to draw only every 3rd point for increased performance.


February 25, 2005
"James Dunne" <jdunne4@bradley.edu> wrote in message news:cvlt7c$2csq$1@digitaldaemon.com...
> In article <cvkk7t$1194$1@digitaldaemon.com>, Ben Hinkle says...
>>
>>> You can't foreach on an AA by itself, you have to foreach on the .keys
>>> or
>>> the
>>> values properties of the AA, which are just regular arrays.
>>
>>yes, you can. for example:
>>import std.stdio;
>>int main() {
>>  int[int] x;
>>  x[100]=1;
>>  x[-100]=2;
>>  x[10]=3;
>>  foreach(int key, int val; x) {
>>    writefln("key %d val %d",key,val);
>>  }
>>  return 0;
>>}
>>
>
> That's a new one to me!  It's not explained in the documentation, so I
> didn't
> think it existed.  Looks like I have to do some research on the foreach
> allowable syntax to see what it's really capable of.  This could just boil
> down
> to a simple documentation problem (or lack thereof).
>
> Regards,
> James Dunne

It would be nice to have more links between related sections of the doc. The
stuff about assoc arrays and foreach is under the "foreach" help. See
http://www.digitalmars.com/d/statement.html#foreach
the paragraph starting with "If the aggregate expression is an associative
array"