October 17, 2006
Reiner Pope wrote:
> Tom S wrote:
>> void each(int[] a, void delegate(int) dg, inout void* ret) {
> It would be good to support non-void delegates as well, for things with predicates, like map, fold, and sort:
> 
>  int fold(int[] a, int start, void delegate(int, int) dg, inout void* ret)
> 
>  and
> 
>  fold(myList, 0) (a, b) { yield a + b; } // Yield returns a value from the delegate, as opposed to returning from the enclosing function

I think it's a great idea and it adds a whole new dimension to the proposal !


> Note that with some predicates shouldn't be able to control the flow of code, so I think that last parameter (inout void* ret) should be optional -- if the last parameter is a delegate, then break and return can't be used within the delegate.

Another good idea :)


>>     each (arr) (int a) {
> Of course, this should be type-inferred so you just need to write
> 
> each (arr) (a) { ... }
>   or
> arr.each (a) { ... }

Sure thing :)


I'm not yet fully convinced if trailing delegates should be allowed implicitly, just because a function's signature matches some criteria. A special keyword like 'trailing' could be used for it. Of course, because of a lack of a better keyword at the moment, we could just call it 'static' <g>


--
Tomasz Stachowiak
October 17, 2006
Chris Miller wrote:
> On Tue, 17 Oct 2006 18:17:33 -0400, Walter Bright <newshound@digitalmars.com> wrote:
> 
>> Frank Benoit (keinfarbton) wrote:
>>> see bug report 440
>>
>> Does that need a fix right away?
> 
> Broke a lot of my projects; went back to DMD 0.169 for now.

Mango, or parts of it, are also broken by this unfortunatly.
October 17, 2006
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.

Stewart.

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:-@ C++@ a->--- UB@ P+ L E@ W++@ N+++ o K-@ w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
October 17, 2006
Walter Bright wrote:
> John Reimer wrote:
>> I have to agree with Sean and Ary.  My own opinion: I don't really
>> understand why "foreach_reverse" was, once again, just tossed into
>> the language with (what seems to be) a minimum of discussion?
> 
> There was quite a bit of discussion. The thread was a bit old, but that doesn't make it less relevant:
> 
> http://www.digitalmars.com/d/archives/digitalmars/D/17320.html
> 
>> Well, will you look at that.  Isn't that irony?  We got our rolls
>> reversed, I think.  I used to be Walter that was hesitant to add
>> keywords to the language.  Go figure! :D
> 
> I was reluctant to do it for a long time for that reason. It's just that no better solution emerged.

To me "for each" had the implication of an unordered traversal, with the undertone that *sometime* when multi-processor machines became more prominent each iteration might be done on a different processor.

I'm aware that this was never the real intent...but that's the subtext that I got when I read the code.  This is partially out of a desire to allow multi-processor systems to be used efficiently.  (What did Intel say?  64 cores/chip in 10 years?)

Perhaps another syntax to denote this will show up.  (Each function or subroutine call, perhaps?  But that doesn't imply parallelism.)

Sorry, just a reaction.
October 17, 2006
Tom S wrote:
> ----
> 
> 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);
> }

That's a lot like how foreach is implemented now <g>.
October 17, 2006
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!
October 18, 2006
Bill Baxter wrote:
> 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:

Ok, I just realized that "delegate" can also be pointer to a non-static nested function...  Duh.  So my question should have been -- why doesn't this work?

int delegate(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))
{...}

Compiles but gives:
"Error: Access Violation"

I'm not totally clear on how pointers/objects are handled in D.  It looks the call to reversed() is maybe creating a copy of the data?  If I put in printfs the pointer value is different from that of the original int[].

--bb
October 18, 2006
Bill Baxter wrote:
> Bill Baxter wrote:
>> 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:
> 
> Ok, I just realized that "delegate" can also be pointer to a non-static nested function...  Duh.  So my question should have been -- why doesn't this work?
> 
> int delegate(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))
> {...}
> 
> Compiles but gives:
> "Error: Access Violation"
> 
> I'm not totally clear on how pointers/objects are handled in D.  It looks the call to reversed() is maybe creating a copy of the data?  If I put in printfs the pointer value is different from that of the original int[].

Basically, when you leave 'reversed', any stack data defined within that function becomes invalid, as D doesn't support real closures. What you're aiming for can be achieved without the new feature anyway, e.g. through:

----
import std.stdio;


struct reverse__(AggregType) {
	alias typeof(AggregType[0]) ElemType;
	AggregType arr;
	int opApply(int delegate(inout ElemType) dg) {
		int ret = 0;
		for (int i = arr.length -1; i >= 0; --i){
			ret = dg(arr[i]);
			if (ret) break;
		}
		return ret;
	}
}

reverse__!(T) reverse(T)(T x) {
	reverse__!(T) res;
	res.arr = x;
	return res;
}


void main() {
	char[] foo = "foo bar";
	foreach (c; foo.reverse) {
		writefln(c);
	}
}
----



--
Tomasz Stachowiak
October 18, 2006
Walter Bright wrote:
> John Reimer wrote:
>> Ah, ok.  I stand corrected on that aspect of my critique.
> 
> I'll give some more lame justifications:
> ...
> So I've been interested in having D algorithms and collections not need iterator types at all. I've been experimenting with doing STL's algorithms in D, and it became clear that to make them complete, reverse iteration was necessary. foreach handles forward iteration, and opIndex handles random access. Various ways of doing reverse traversal without core support always seemed to involve creating dummy classes and other hackish stuff. Promoting it to a supported statement makes it pretty clean for the user to understand and use.
> 
> Essentially, I think foreach_reverse is the missing piece to be able to implement all of STL's algorithms code for D.

I don't see how it helps.  If you can already do:
   foreach(T item; &collection.reversed()) { }

isn't that alone enough to be able to implement all of STL's algorithms without adding a "foreach_reverse"?

In fact I'd argue that adding foreach_reverse does nothing more to make STL algorithms implementable.  If I'm trying to implement something like std::copy, how is foreach_reverse going to help me do that generically?  Users can't pass 'foreach_reverse' in as an argument if they want to make a backwards copy of something.  But they can certainly pass &collection.reversed() in as one.

And -- wouldn't it be nice if the original designer of the class forgot to write a reversed(), if I could write one and do
   foreach(T item; reversed(collection)) { }
(without dummy classes and hackish stuff being required). :-)

--bb
October 18, 2006
Tom S wrote:
> Bill Baxter wrote:
>> Bill Baxter wrote:
>>> 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:
>>
>> Ok, I just realized that "delegate" can also be pointer to a non-static nested function...  Duh.  So my question should have been -- why doesn't this work?
>>
>> int delegate(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))
>> {...}
>>
>> Compiles but gives:
>> "Error: Access Violation"
>>
>> I'm not totally clear on how pointers/objects are handled in D.  It looks the call to reversed() is maybe creating a copy of the data?  If I put in printfs the pointer value is different from that of the original int[].
> 
> Basically, when you leave 'reversed', any stack data defined within that function becomes invalid, as D doesn't support real closures. What you're aiming for can be achieved without the new feature anyway, e.g. through:
> 
> ----
> import std.stdio;
> 
> 
> struct reverse__(AggregType) {
>     alias typeof(AggregType[0]) ElemType;
>     AggregType arr;
>     int opApply(int delegate(inout ElemType) dg) {
>         int ret = 0;
>         for (int i = arr.length -1; i >= 0; --i){
>             ret = dg(arr[i]);
>             if (ret) break;
>         }
>         return ret;
>     }
> }
> 
> reverse__!(T) reverse(T)(T x) {
>     reverse__!(T) res;
>     res.arr = x;
>     return res;
> }
> 
> 
> void main() {
>     char[] foo = "foo bar";
>     foreach (c; foo.reverse) {
>         writefln(c);
>     }
> }

Did you mean
   foreach (c; reverse(foo)) ??
I guess so.  it does seem to compile that way.

I think this falls into Walter's "dummy classes and hackish stuff" category though.

Is there no way to make something similar work with a nested function? If my reversed function above could just get the pointer to the actual array then it seems like it should work.  Is there some way to declare the AggregateT array parameter so that that happens?

--bb