October 18, 2006
Oskar Linde wrote:
> Walter Bright wrote:
> 
>> Bill Baxter wrote:
>>
>>> I don't see how it helps.  If you can already do:
>>>    foreach(T item; &collection.reversed()) { }
>>
>>
>> That doesn't work for arrays.
> 
> 
> In what way does it not work? I have been doing:
> 
> foreach(x; "abcd".reverseView())
>     writef("%s",x);
> 
> prints "dcba"
> 
> For any type of built in array for a very long time (long before 0.170), and it certainly seems to work for me.

How do you do that?  I just get
: undefined identifier reverseView
: function expected before (), not reverseView of type int
: cannot infer type for x
October 18, 2006
Walter Bright wrote:
> Bill Baxter wrote:
>> I don't see how it helps.  If you can already do:
>>    foreach(T item; &collection.reversed()) { }
> 
> That doesn't work for arrays.

Is "foreach_reverse" just there for arrays? To me it dosen't bother to have a new keyword, but if it just exists to do:

for(int i = array.length - ; i >= 0; i--) {
	// ...
}

then it is realy superflous.

"foreach_reversed" just is useful for two things: arrays and lists. For arrays you can always type the code above. For lists, give them a "reversed" method that accepts a delegate, as I wrote. For other types the "foreach" dosen't guarantee the order! (map, tree, set, etc.)

So "for(;;)" appears 5% of the time, convert it to "forever". :-)

I repeat: is "foreach_reverse" just there for arrays?
October 18, 2006
Ary Manzana wrote:
> Walter Bright wrote:
> 
>> Bill Baxter wrote:
>>
>>> I don't see how it helps.  If you can already do:
>>>    foreach(T item; &collection.reversed()) { }
>>
>>
>> That doesn't work for arrays.
> 
> 
> Is "foreach_reverse" just there for arrays? To me it dosen't bother to have a new keyword, but if it just exists to do:
> 
> for(int i = array.length - ; i >= 0; i--) {
>     // ...
> }
> 
> then it is realy superflous.
> 
> "foreach_reversed" just is useful for two things: arrays and lists. For arrays you can always type the code above. For lists, give them a "reversed" method that accepts a delegate, as I wrote. For other types the "foreach" dosen't guarantee the order! (map, tree, set, etc.)
> 
> So "for(;;)" appears 5% of the time, convert it to "forever". :-)
> 
> I repeat: is "foreach_reverse" just there for arrays?


No, it at least works for arrays and any class with an "opApplyReverse" method, and for any delegate you feel like passing in.

Thus you can even do something nonsensical like:

   foreach_reverse(int i; &aggregate.opApply()) { }

And use foreach_reverse to iterate forwards over aggregate.  The only thing foreach_reverse gets you is the ability to magically call the magic method "opApplyReverse" on an object that has it.  Otherwise foreach_reverse is pretty much identical to foreach.  Oh, and I guess you get the ability to iterate backwards over the built-in types, too, which don't actually have opApply/opApplyReverse methods, but I think their lack of those methods is a missed opportunity.  If they just had opApply (or acted like they did) then there wouldn't be any need to make special case rules for them in the language.

So basically, without foreach_reverse you can set it up so you can do reverse iteration with something like:
    foreach(int i; &aggregate.reversed())
or
    foreach(int i; reversed(aggregate))

With it, you get to say
    foreach_reverse(int i; aggregate)
which actually is equivalent to
    foreach_reverse(int i; &aggregate.opApplyReverse())
which is also equivalent to
    foreach(int i; &aggregate.opApplyReverse())

Basically foreach_reverse is 99% identical to foreach, but whereas foreach has a secret pact with all classes to call their opApply method, foreach_reverse has a secret pact to call opApplyReverse.  In all other respects they are identical.  Really either one can be made to iterate any direction and any way you wish.  foreach can iterate backwards if you want, and foreach_reverse can iterate forwards.

It is pretty much the antithesis of orthogonality to have multiple, very similar constructs offeing very nearly identical functionality.  And meanwhile poor old 'static' is made to do the jobs of twenty big men because we can't afford another keyword to lighten his workload.  Yet foreach_reverse gets a seat at the big boys' table just for knowing how to call opApplyReverse()?

Doesn't seem reasonable to me.

But it could be worse.  At least foreach_reverse doesn't steal your socks or eat babies.  He is pretty easy to ignore if you don't like him.

--bb
October 18, 2006
Bill Baxter wrote:
> Oskar Linde wrote:
>> Walter Bright wrote:
>>
>>> Bill Baxter wrote:
>>>
>>>> I don't see how it helps.  If you can already do:
>>>>    foreach(T item; &collection.reversed()) { }
>>>
>>>
>>> That doesn't work for arrays.
>>
>>
>> In what way does it not work? I have been doing:
>>
>> foreach(x; "abcd".reverseView())
>>     writef("%s",x);
>>
>> prints "dcba"
>>
>> For any type of built in array for a very long time (long before 0.170), and it certainly seems to work for me.
> 
> How do you do that?  I just get
> : undefined identifier reverseView
> : function expected before (), not reverseView of type int
> : cannot infer type for x

Sorry. I wasn't very clear on this in my post. Here is a simple implementation that runs:

struct ReverseIterator(T:T[]) {
    T[] array;

    int opApply(int delegate(inout T) dg) {
        for (int i = array.length-1; i >= 0; i--) {
            if (auto status = dg(array[i]))
                return status;
        }
        return 0;
    }
}

ReverseIterator!(Array) reverseView(Array)(Array array) {
    ReverseIterator!(Array) iter; // = {array} => ICE
    iter.array = array;
    return iter;
}

import std.stdio;
void main() {
    foreach(x; "abcd".reverseView())
        writef("%s",x);
}
October 18, 2006
Walter Bright wrote:
> Stewart Gordon wrote:
> 
>> Vladimir Kulev wrote:
>>
>>> J Duncan wrote:
>>>
>>>> Disagree with you, its a nice thing to have.
>>>
>>>
>>> There are many things which are nice to have, but only really essential ones
>>> should be in language itself. Reverse foreach can be implemented in other
>>> way.
>>
>>
>> So can ordinary foreach.
> 
> 
> All you really need is if and goto!


no, all you really need is goto and label variables

int i = 10;
loop:
	i--;
	goto [loop, quit][cast(int)(i==0)];
quit:
October 18, 2006
Frits van Bommel wrote:
> Walter Bright wrote:
> 
>> Hasan Aljudy wrote:
>>
>>> Also, since foreach already takes two arguments (int i, T t) that is, index and element, you can add a third argument, of bool type, with the meaning "start in reverse mode"
>>> foreach( true, i, d; "Hello" )
>>> {
>>>     writefln( d );
>>> }
>>>
>>> should print:
>>> o
>>> l
>>> l
>>> e
>>> H
>>
>>
>> That would work, and something like it has been suggested before, but it is too obscure looking. When someone sees "foreach_reverse", I think it'll be very clear what's going on.
> 
> 
> There's no need to use booleans (at least, not directly):
> 
> enum Ordering : bool { forward, backwards, reversed = backwards }
> 
> That should produce much more readable code, but work the same. Can be done right now, except you can't overload opApply for arrays :(. (Tell me if I'm wrong, but I tried and it didn't work)


IIRC something like this should work

<code>
int go(char[], int delegate(inout char [, ...]) dg)
{
}

void main()
{
	foreach(char c; &("hello world".go)){}
}
</code>


if not, this should work:


<code>
template reverse(T)
{
	int delegate(int delegate(inout T)) reverse(T[] stuff)
	{
		auto ret = new struct
		{
			T[] data;
			int go(int delegate(inout T) dg)
			{
				for (int i = data.length-1; i >= 0; i--)
				{
					if(int result = dg(data[i]);
						return result;
				}
				return 0;
			}
		}
		ret.data = stuff;
		return &ret.go;
	}
}


...

	foreach(char c; reverse("hello world"));
	{
		...
	}
</code>
// I havent tried either so I expect there to be a few errors in them.
October 18, 2006
Oskar Linde wrote:
> One example of a generalizing addition is Tomasz' suggestion for trailing delegates. It generalizes foreach, foreach_reverse and even the while-loop. Such an addition would not only make libraries simpler and more powerful.

foreach_reverse is about as simple as one can get, from a user standpoint. It also works efficiently with arrays, which is hugely important because most of the time it will be used for arrays.

> It could also simplify the core language.

foreach_reverse was pretty trivial to implement. All the machinery was already there.

> What is design if not the pursuit of simplicity?

Simplicity is sometimes an elusive goal. For example, D started out with a simple syntax for function literals. Nobody used it, nobody even noticed it, people kept asking for the feature even after I pointed it out to them that D had it. Then, I figured out a way to eliminate some of the extra syntax for it, and voila! suddenly it gets noticed. This is even though the newer style is harder to parse and implement.

I also remember a thread here about D versus Ruby, and how everything was so simple in Ruby. It didn't matter that I showed how it could be done in D, it appeared to be simpler in Ruby, and that apparently made all the difference.
October 18, 2006
Oskar Linde wrote:
> Walter Bright wrote:
>> Bill Baxter wrote:
>>> I don't see how it helps.  If you can already do:
>>>    foreach(T item; &collection.reversed()) { }
>>
>> That doesn't work for arrays.
> 
> In what way does it not work? I have been doing:
> 
> foreach(x; "abcd".reverseView())
>     writef("%s",x);
> 
> prints "dcba"

Try that, along with foreach_reverse, and look at the generated code.
October 18, 2006
"BCS" <BCS@pathlink.com> wrote in message news:eh5kki$11t$4@digitaldaemon.com...

> no, all you really need is goto and label variables
>
> int i = 10;
> loop:
> i--;
> goto [loop, quit][cast(int)(i==0)];
> quit:

Who needs flow control?  If you need n iterations, copy and paste n times!


October 18, 2006
Walter Bright wrote:
> Oskar Linde wrote:
>> One example of a generalizing addition is Tomasz' suggestion for trailing delegates. It generalizes foreach, foreach_reverse and even the while-loop. Such an addition would not only make libraries simpler and more powerful.
> 
> foreach_reverse is about as simple as one can get, from a user standpoint. It also works efficiently with arrays, which is hugely important because most of the time it will be used for arrays.

For what it's worth, I think iterative operation requirements all fall into one of three categories: forward, reverse, and unordered.  Forward iteration is by far the most common, so if semantic differences are involved, it should obviously be the prettiest ;-)  Reverse iteration is not terribly common, but it does find occasional use in search-oriented algorithms.  Unordered (for lack of a better term) can be used to describe any operation which has no ordering requirement and simply must be applied to all elements in a sequence.

I believe it is important that the compiler be able to recognize forward and reverse iteration at compile-time so optimizations may be applied. After all, that's the entire point of a built-in foreach in the first place.  Also, the compiler should be allowed to degenerate both forward and reverse iteration to the third category, unordered, when it can determine that visitation order has no impact on the operations being performed.  Some criteria for this may be if the statement block does not contain break, continue, or goto statements, and if all operations on sequence data are commutative.  Optionally, in instances where a statement block may not qualify for auto-degeneration, the user should be able to specify that it should be used anyway.  This would also allow user-defined types to implement unordered operations in non-trivial situations.  So we have something like this:

foreach() // the default: forward iteration, compiler may degenerate
foreach!(fwd)() // forward iteration, compiler may degenerate
foreach!(rev)() // reverse iteration, compiler may degenerate
foreach!(any)() // unordered: SSE ops may be used, concurrency, etc
foreach!(fwd,strict)() // forward iteration, compiler may not degenerate
foreach!(rev,strict)() // reverse iteration, compiler may not degenerate
foreach!(any,strict)() // unordered, same as without strict

(the above code above isn't intended to suggest syntax so much as to describe the operations and restrictions I think may eventually be useful)

Does this sound reasonable?  And can anyone suggest a cleaner syntax?


Sean