Jump to page: 1 2
Thread overview
DMD 0.71 release
Sep 03, 2003
Walter
Re: DMD 0.71 release (fixed ?read? and append)
Sep 04, 2003
And
Sep 04, 2003
Walter
Sep 04, 2003
Mike Wynn
Sep 04, 2003
Walter
Sep 05, 2003
Sean L. Palmer
Sep 05, 2003
Walter
Sep 06, 2003
Sean L. Palmer
Sep 06, 2003
Daniel Yokomiso
Sep 05, 2003
Mike Wynn
Sep 05, 2003
Walter
Sep 05, 2003
Mike Wynn
Sep 06, 2003
Walter
Sep 07, 2003
Mike Wynn
Sep 06, 2003
Sean L. Palmer
Sep 04, 2003
Sean L. Palmer
Sep 04, 2003
Walter
September 03, 2003
This includes the new foreach construct, which is the building block for container classes and iterations. I'll work on the bug reports next!

http://www.digitalmars.com/d/changelog.html



September 04, 2003
In article <bj5pa5$156u$1@digitaldaemon.com>, Walter says...
>
>
>This includes the new foreach construct, which is the building block for container classes and iterations. I'll work on the bug reports next!
>
>http://www.digitalmars.com/d/changelog.html
>
>
>
Fixed bug with linux file.read() and file.append() functions.

I believe you mean write and append.

Ant


September 04, 2003
"And" <And_member@pathlink.com> wrote in message news:bj6ds0$212l$1@digitaldaemon.com...
> I believe you mean write and append.

Yes, thanks.


September 04, 2003
Walter wrote:
> This includes the new foreach construct, which is the building block for
> container classes and iterations. I'll work on the bug reports next!
> 
> http://www.digitalmars.com/d/changelog.html
> 

while I like the inclusion of foreach,
would it not be a little nicer if it was

foreach( <type> <name> (in|inout) <expr>) <statement>
i.e.

void func( char[] str ) {
	foreach( char c in str ) {
		// modifying c does not effect str[i]
	}
	foreach( char c inout str ) {
		modifying c modifies str[i];
	}
}

the apply member:
why is it not
void apply( bit delegate( in T ) ) { .. } // foreach in
void apply( bit delegate( inout T ) ) { .. } // foreach inout.

the apply delegate returns true if the iteration should continue, I don't see the need for apply to return 0 or for the delegate to return an int

implemented as
class canForeach {
	char[] str;
	void apply( bit delegate( in char ) applyfunc ) {
		for( int i = 0; i < str.length; i++ ) {	
			if ( !applyfunc( str[i] ) ) { return; }
		}
	}
}



September 04, 2003
Yup, that's pretty much what I had in mind.

Is there a way to obtain the 'type' of the elements to be iterated over automatically?  It should be possible to infer the type.

Sean

"Walter" <walter@digitalmars.com> wrote in message news:bj5pa5$156u$1@digitaldaemon.com...
>
> This includes the new foreach construct, which is the building block for container classes and iterations. I'll work on the bug reports next!
>
> http://www.digitalmars.com/d/changelog.html


September 04, 2003
"Mike Wynn" <mike@l8night.co.uk> wrote in message news:bj7gn4$gtb$1@digitaldaemon.com...
> while I like the inclusion of foreach,
> would it not be a little nicer if it was
>
> foreach( <type> <name> (in|inout) <expr>) <statement>
> i.e.
>
> void func( char[] str ) {
> foreach( char c in str ) {
> // modifying c does not effect str[i]
> }
> foreach( char c inout str ) {
> modifying c modifies str[i];
> }
> }

I thought about making it compatible with function parameter syntax that uses inout.

> the apply member:
> why is it not
> void apply( bit delegate( in T ) ) { .. } // foreach in
> void apply( bit delegate( inout T ) ) { .. } // foreach inout.
>
> the apply delegate returns true if the iteration should continue, I don't see the need for apply to return 0 or for the delegate to return an int
>
> implemented as
> class canForeach {
> char[] str;
> void apply( bit delegate( in char ) applyfunc ) {
> for( int i = 0; i < str.length; i++ ) {
> if ( !applyfunc( str[i] ) ) { return; }
> }
> }
> }

The need for it to return an int rather than bit is because of the compiler 'magic' involved with making this work. The foreach body is transformed into a nested function (!). The return value is needed to handle the various ways of exiting the foreach body, such as goto, break label, return, etc. The foreach itself is replaced with a switch statement calling the apply function. It sounds a bit hairy, but that's all hidden from the programmer. The neato thing is there is no need to write state-preserving iterators, a rather tricky thing to get right for containers that are recursive or otherwise need state-preserving iterators.


September 04, 2003
"Sean L. Palmer" <palmer.sean@verizon.net> wrote in message news:bj7pc7$tie$1@digitaldaemon.com...
> Yup, that's pretty much what I had in mind.
>
> Is there a way to obtain the 'type' of the elements to be iterated over automatically?  It should be possible to infer the type.

No. It sounds like a good idea, though.


September 05, 2003
"Walter" <walter@digitalmars.com> wrote in message news:bj84jb$1e41$1@digitaldaemon.com...

> The need for it to return an int rather than bit is because of the
compiler
> 'magic' involved with making this work. The foreach body is transformed
into
> a nested function (!). The return value is needed to handle the various
ways
> of exiting the foreach body, such as goto, break label, return, etc. The foreach itself is replaced with a switch statement calling the apply function. It sounds a bit hairy, but that's all hidden from the
programmer.
> The neato thing is there is no need to write state-preserving iterators, a rather tricky thing to get right for containers that are recursive or otherwise need state-preserving iterators.

This is pretty cool, Walter.  It sounds to me as if you're halfway to making general inplace anonymous functions!

Will it inline these calls to the nested function if everything is final?

And you can, say, break out of a foreach?  This (behind the scenes) returns a certain value from the nested function?

One nice thing about having actual iterators is that you can form a range over which to iterate.  It doesn't sound like this setup allows that... it just allows sequentially iterating from the beginning until the Apply() function decides to return a "stop" value or it hits the end, correct? Granted, 90% of the cases would want to iterate the entire container, but in the other 10% it may prove a bit constrictive.

Combined with not being able to overload operator [] means it's very hard to make a good usable array class that looks and feels like a builtin. Everybody will do it differently.  Usually with array-like things you can use operator [].  So by providing operator [] and a size() function you can emulate an array, and by providing the iterator functions you can mimic a list or generic forward-iterating container.  foreach would try to use the iterators if available and if not, default to indexing 0 .. size-1 and using operator []?

How is foreach going to work with user-defined containers?

Sean


September 05, 2003
"Sean L. Palmer" <palmer.sean@verizon.net> wrote in message news:bj9eod$aps$1@digitaldaemon.com...
> Will it inline these calls to the nested function if everything is final?

No, since it is done as a pointer to a function.

> And you can, say, break out of a foreach?  This (behind the scenes)
returns
> a certain value from the nested function?

Yes.

> One nice thing about having actual iterators is that you can form a range over which to iterate.  It doesn't sound like this setup allows that... it just allows sequentially iterating from the beginning until the Apply() function decides to return a "stop" value or it hits the end, correct? Granted, 90% of the cases would want to iterate the entire container, but
in
> the other 10% it may prove a bit constrictive.

The secret to modifying this for specific cases is to override/modify the apply(). Here's a little template technique to run an array in reverse:


template ReverseIterator(T : T[])
{
    class RI
    {
 T[] a;

 this(T[] a)
 {
     this.a = a;
 }

 int apply(int delegate(inout T v) dg)
 {   int result;

     for (int i = a.length; i; i--)
     { result = dg(a[i - 1]);
  if (result)
      break;
     }
     return result;
 }
    }
}

void main()
{
    int[3] a;

    a[0] = 1;
    a[1] = 2;
    a[2] = 3;

    foreach (int i; new instance ReverseIterator(int[]).RI(a))
    {
 printf("i = %d\n", i);
    }
}


> Combined with not being able to overload operator [] means it's very hard
to
> make a good usable array class that looks and feels like a builtin.

I need to provide [] overloads, no question about it.

> Everybody will do it differently.  Usually with array-like things you can use operator [].  So by providing operator [] and a size() function you
can
> emulate an array, and by providing the iterator functions you can mimic a list or generic forward-iterating container.  foreach would try to use the iterators if available and if not, default to indexing 0 .. size-1 and
using
> operator []?
> How is foreach going to work with user-defined containers?

All the container needs is an apply() function - no need for iterators. I have never particularly liked the iterator paradigm, as it just involves lots of cruft to write. Things get really ugly when trying to write an iterator for a recursive structure, articles on iterators tend to ignore these cases and show instead the trivial ones. The C# people seem to be trying to get around this by using coroutines, but the complexities of that are just too awful. There hasn't been a foreach in D before because I just could never figure out a solution that I liked, and then finally had a brainwave about using a magic nested function.

The more I use nested functions, it turns out, the more useful they become <g>.


September 05, 2003
Walter wrote:
> 
> The need for it to return an int rather than bit is because of the compiler
> 'magic' involved with making this work. The foreach body is transformed into
> a nested function (!). The return value is needed to handle the various ways
> of exiting the foreach body, such as goto, break label, return, etc. The
> foreach itself is replaced with a switch statement calling the apply
> function. It sounds a bit hairy, but that's all hidden from the programmer.
> The neato thing is there is no need to write state-preserving iterators, a
> rather tricky thing to get right for containers that are recursive or
> otherwise need state-preserving iterators.
> 
why not use a local like this ..

void func( SomeObj ar ) {
outer: {
  ......
	foreach( int a; ar ) {
		printf( "%d ", a );
		if ( a == '\n' ) { break outer; }
	}
  } // outer loop
}
... becomes ...
void func( SomeObj ar ) {
 outer: {
     .....
	{ // foreach
		int __iter_000_state = 0;
		bit irf( int a ) {
			printf( "%d", a );
			if ( a == '\n' ) {
				__iter_000_state = BREAK_000;
			// update parent stack frame
				return false;
			}
			return true;
		} // end iterator func
		ar.apply( irf );
		switch( __iter_000_state ) {
		default: break;
		BREAK_000: break outer;
		}
	}
   } // outer
}

simple break is just return false (stop iterating)
simple continue is just return true (stop current iteration, but continue)

I agree this is a little slower, BUT removes the "magic" from the programmers view, I feel the int magic although a little faster does expose the inner workings a little too much and is a bug waiting to happen.





« First   ‹ Prev
1 2