October 17, 2006
Vladimir Kulev wrote:
> Agree with you, foreach_reverse is unnecessary feature, so it should not
> exist.

Disagree with you, its a nice thing to have.
October 17, 2006
Is there any reason for not allowing a function to be used too?
Then you could also use a closure as the thing that does the iteration:

int function(int delegate(inout ElemT)) reversed(AggregateT,ElemT)(AggregateT array)
{
    int _innerFunc(int delegate(inout ElemT) loopBody)
    {
        int done = 0;
        for (int i = array.length-1; i >=0; i--)
        {
            done = loopBody(array[i]);
            if (done)
                break;
        }
        return done;
    }
    return &_innerFunc;
}
...
foreach(real r; reversed!(real[],real)(areal))
{...}

--bb


Bill Baxter wrote:
> Bruno Medeiros wrote:
> 
>> Walter Bright wrote:
>>
>>> Added foreach_reverse, which addresses a serious shortcoming.
>>>
>>> http://www.digitalmars.com/d/changelog.html
>>
>>
>>
>> foreach_reverse addresses a serious shortcoming? What is that, if instead of:
>>   foreach_reverse(Foo f; aggregate) { ...
>> I can do:
>>   foreach(Foo f; &aggregate.opApplyReverse) { ...
>>
>> The latter form is both more general (allows any kind of iterators) and more simple/orthogonal (no extra special statements are needed).
> 
> 
> I agree.  Allowing any delegate to be used is a good move I think.  But the incremental value added by 'foreach_reverse' and 'opApplyReverse' are minimal once you can already use any delegate as the aggregate.
> 
> It would be nice to be able to write a function like "reversed" and then just do
>     foreach(Foo f; reversed(aggregate)) { ...
> 
> To me that seems like a more clean and natural way to support a variety of iteration strategies without adding special-cased reverse iterators to the language.
> 
> Here's a somewhat limited version of that idea:
> It's limited by my lack of template-fu, so it just works for arrays, and you have to explicitly pass too many template parameters:
> 
> class _rev_proxy(AggregateT, ElemT)
> {
>     this(AggregateT theObj) {
>         obj = theObj;
>     }
>     int opApply(int delegate(inout ElemT) dg)
>     {
>         int done = 0;
>         for (int i = obj.length-1; i >=0; i--)
>         {
>             done = dg(obj[i]);
>             if (done)
>                 break;
>         }
>         return done;
>     }
>   private:
>     AggregateT obj;
> }
> 
> _rev_proxy!(AggregateT,ElemT) reversed(AggregateT,ElemT)(AggregateT array)
> {
>     return new _rev_proxy!(AggregateT,ElemT)(array);
> }
> 
> unittest
> {
>     void testrev() {
>         int[] aint = [1,2,3,4,5,6,7];
>         real[] areal = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
> 
>         foreach(int i; reversed!(int[],int)(aint))
>         {
>             printf("%d\n",i);
>         }
> 
>         foreach(real r; reversed!(real[],real)(areal))
>             //foreach(real r; areal)
>         {
>             printf("%f\n",cast(float)(r));
>         }
>     }
>     testrev();
> }
> 
> 
> I would guess this could probably be expanded with more template-fu so that any class which supports a particular protocol (e.g. has a .length and [] operator, or a reversed() method) can automatically be reversed.  And then as a last resort you could always write your own specialization of reversed!() particularly for your class.
> 
> --bb
October 17, 2006
Ary Manzana wrote:



 class Tree {

     int inorder(int delegate(inout int)) {
             // inorder traversal
     }

     int preorder(int delegate(inout int)) {
             // preorder traversal
     }

    int postorder(int delegate(inout int)) {
             // postorder traversal
     }

 }

 void main() {
     Tree t = giveMeSomeTree();

     foreach(int i : &t.inorder) {
         // something
         }

     foreach(int i : &t.preorder) {
         // something
         }

     foreach(int i : &t.postorder) {
         // something
         }

 }

If I understand correctly, the above works with what is there now.

The things with the foreach_reverse is (I think) mostly for actuals arrays, and the opApplyReverse is really there more for orthogonality than need. that orthogonality has some cool side effects* so I'd say leave them in.

*: I have noticed recently that good orthogonality allows for some vary cool compile time options (think code generation). It almost lets c/c++/d act as dynamicly typed languages.
October 17, 2006
interface IFoo
{
   void foo();
}

class Foo: IFoo
{
   final void foo() { }
}

DMD 0.170 output:
   test.d(6): class test.Foo interface function IFoo.foo isn't implemented

Is this really what should happen? I thought final could mean it's still virtual if it's an interface implementation function or overriding something from a base class, just that it cannot be overridden again.
October 17, 2006
Lars Ivar Igesund wrote:
> Walter Bright wrote:
> 
> 
>>Added foreach_reverse, which addresses a serious shortcoming.
>>
>>http://www.digitalmars.com/d/changelog.html
> 
> 
> But foreach_reverse is such an ugly name, then I like 'reveach' (courtesy of
> Mr Panek) much better :P
> 
I disagree. foreach_reverse is clearer.
-DavidM
October 17, 2006
see bug report 440
October 17, 2006
On Tue, 17 Oct 2006 12:43:41 -0400, Frank Benoit (keinfarbton) <benoit@tionex.removethispart.de> wrote:

> see bug report 440

oh, thanks; I wasn't sure if it was a bug. If so, I agree that it's high priority.
October 17, 2006
Lars Ivar Igesund wrote:
> I'm just trying to play the devil's (that's Tom) advocate here ;) What I
> think was shown by Tom, wasn't that the new feature isn't damn useful, but
> that there might be better, more flexible and more powerful ways to
> implement the feature, if it is possible with templates, then the sugar
> shouldn't be any worse, and the possibilities one could gain for D in
> general could be substantial.


Thanks, Lars :) Indeed, I'm not saying that the feature is superfluous, I'm just trying to find a better way. For instance, the older 'trailing delegate' proposal could make foreach obsolete by allowing totally custom foreach-alike loops to be created. Last time I mentioned it, Walter seemed to like the proposal, but there was one open problem - namely that if I had code like:

void myLoop(void delegate() dg) {
	dg();
	...
	dg();
}

myLoop {		// special syntax sugar for trailing delegates
	return;
}


then the 'return' would actually only return from the delegate, not from the scope that encloses myLoop. This and break wouldn't work for custom loops.

But after a while of brainstorming on #d, I return with another proposal. D could solve the problem by more or less translating the code:

----

void each(int[] a, void delegate(int) dg, inout void* ret) {
	for (int i = 0; i < a.length; ++i) {
		dg(a[i]);
		if (ret) return;
	}
}


char[] foo() {
	int[] arr;
	arr ~= 5;
	arr ~= 3;
	arr ~= 2;
	arr ~= 6;

	each (arr) (int a) {
		if (1 == a) return "blah";
		if (2 == a) return "hmm";
		if (3 == a) break;
	}

	return null;
}


void main() {
	char[] str = foo();
	printf("foo returned: %.*s\n", str);
}

----


into:


----

void* CONTINUE	= cast(void*)0;
void* BREAK		= cast(void*)1;


void each(int[] a, void delegate(int) dg, inout void* ret) {
	for (int i = 0; i < a.length; ++i) {
		dg(a[i]);
		if (ret) return;
	}
}


char[] foo() {
	int[] arr;
	arr ~= 5;
	arr ~= 2;
	arr ~= 3;
	arr ~= 6;

	{
		char[]	retCode;
		void*	ret;

		each(arr, (int a) {
			if (1 == a) { retCode = "blah";	ret = &retCode; return; }
			if (2 == a) { retCode = "hmm";	ret = &retCode; return; }
			if (3 == a) { ret = BREAK; return; }
		}, ret);

		if (ret !is BREAK && ret !is CONTINUE) return *cast(char[]*)ret;
	}

	return null;
}


void main() {
	char[] str = foo();
	printf("foo returned: %.*s\n", str);
}

----


Ok, what happened there ? The 'each' function takes two special parameters:
1. a delegate with no return
2. an inout void*:
in this case, `void delegate(int) dg, inout void* ret`.

They define the trailing delegate - 'dg' is the delegate's body and 'ret' is a special value used to communicate with the enclosing scope. The void* could be moved to the function and delegate's return type, but in that case, the custom loop function couldn't return anything to the enclosing scope. With this approach, e.g. the number of iterations can be returned.

There's nothing special about the 'each' function itself, the only magic happens at its call site. In the custom loop's body,
each 'break' is converted into '{ ret = BREAK; return; }',
each 'continue' into '{ ret = CONTINUE; return; }'
and each 'return X' into '{ retCode = X; ret = &retCode; return; }

After the custom loop returns, its 'ret' is checked to see whether the return from the loop (in this case, the 'each' function) was a CONTINUE, BREAK or a return from the enclosing scope. In the latter case, that value is returned from the call site.

I hope I've covered every spot, but the proposal may still have bugs... Anyway, thanks goto Oskar Linde for helping me come up with this on #d !


--
Tomasz Stachowiak
October 17, 2006
Lars Ivar Igesund wrote:
> Bruno Medeiros wrote:
> 
>> Walter Bright wrote:
>>> Added foreach_reverse, which addresses a serious shortcoming.
>>>
>>> http://www.digitalmars.com/d/changelog.html
>> foreach_reverse addresses a serious shortcoming? What is that, if
>> instead of:
>>    foreach_reverse(Foo f; aggregate) { ...
>> I can do:
>>    foreach(Foo f; &aggregate.opApplyReverse) { ...
>>
>> The latter form is both more general (allows any kind of iterators) and
>> more simple/orthogonal (no extra special statements are needed).
>>
> 
> Good point and ties in with Tom's post; both of these features
> (foreach_reverse and delegate-as-aggregate) could be easily implemented
> using other constructs with some "fixes" to the core language.
> 

+1 foreach(Foo f; &aggregate.opApplyReverse) seems a much more powerful form
October 17, 2006
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.