October 31, 2006
Lionello Lunesu wrote:
> I got so used to foreach in D, I've just ported it to C ;)
> 
> namespace D {
> template <typename T, int S>
> inline size_t length( T (&a)[S] ) { return S; }
> // Add overloads for your own containers
> }
> 
> #define foreach(ELEMENT,ARRAY) \
>   for(size_t __L=D::length(ARRAY),__B=0,__I=0; !__B && __I<__L; assert(__L==D::length(ARRAY)), ++__I ) \
>     if (__B = 1) \
>       for(ELEMENT = ARRAY[__I]; __B; __B=0) // a break must make the main loop stop

I think you have a dangling-else bug here:

	if (whatever)
		foreach(elt, arr) { something(); }
	else
		somethingelse();

What happens in this code? It looks like somethingelse() will be executed if whatever evaluates to false, but IIRC the 'else' clause will bind to the 'if' you have in your macro. Since that one never evaluates to false, somethingelse() will never be called here.

I haven't tried it though, this was just from looking at it.
Also, this won't manifest itself if you always use braces after 'if's so this may be hard to spot the one time you forget :).
October 31, 2006
Frits van Bommel wrote:
> Lionello Lunesu wrote:
>> I got so used to foreach in D, I've just ported it to C ;)
>>
>> namespace D {
>> template <typename T, int S>
>> inline size_t length( T (&a)[S] ) { return S; }
>> // Add overloads for your own containers
>> }
>>
>> #define foreach(ELEMENT,ARRAY) \
>>   for(size_t __L=D::length(ARRAY),__B=0,__I=0; !__B && __I<__L; assert(__L==D::length(ARRAY)), ++__I ) \
>>     if (__B = 1) \
>>       for(ELEMENT = ARRAY[__I]; __B; __B=0) // a break must make the main loop stop
> 
> I think you have a dangling-else bug here:
> 
>     if (whatever)
>         foreach(elt, arr) { something(); }
>     else
>         somethingelse();
> 
> What happens in this code? It looks like somethingelse() will be executed if whatever evaluates to false, but IIRC the 'else' clause will bind to the 'if' you have in your macro. Since that one never evaluates to false, somethingelse() will never be called here.
> 
> I haven't tried it though, this was just from looking at it.
> Also, this won't manifest itself if you always use braces after 'if's so this may be hard to spot the one time you forget :).

Good one, easily solvable though, by inverting __B and using "if () {} else for...".. Thanks!

L.
October 31, 2006
Bill Baxter wrote:
> Reiner Pope wrote:
>> I don't see much reason to change the syntax here, since:
>>  - it provides no generalizations/abstractions
> 
> It generalizes 'for'.
> 

No it doesn't. It provides no semantic generalization/orthogonality *as well as* no syntactic generalization/orthogonality either. If you think of the D grammar, the 'foreach' rule is removed, but another, similar one is added to the 'for' rule alternatives.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
October 31, 2006
Lionello Lunesu wrote:
> I got so used to foreach in D, I've just ported it to C ;)

I believe Eric Niebler wrote one too for C++ using preprocessor wizardry. I've never been able to figure out how it worked <g>.
November 01, 2006
Bill Baxter wrote:
> Walter Bright wrote:
>> Bill Baxter wrote:
>>> Just like some people have said, you can do everything with 'for'.
>>> Well, actually maybe you can.  It occurred to me that foreach and for really don't clash.  One requires two semicolons, the other only one. So instead of a foreach we could have had
>>>
>>>   for(a; aggregate)
>>>   {
>>>
>>>   }
>>
>> That looks like a syntax error for one used to for loops.
> 
> For one used to C for loops, yes.  But Python for loops look like
>   for a in aggregate: ...
> Bash for loops look like
>   for f in list; do ... done
> 
> There is certainly precedent for using 'for' synonymously with 'for each'.

Who is more likely to move to D - a C/C++ programmer who also knows Python/Bash, or a dedicated Python or Bash programmer?

>>> or if the keyword 'in' had been used for the new construct instead of borrowing from c's ; syntax it would be:
>>>
>>>   for(a in aggregate)
>>>   {
>>>      ...
>>>   }
>>
>> 'in' is already an operator, so that wouldn't work.
> 
> It already has multiple duties as a storage type, so that alone shouldn't be an issue.
> But I suppose you're right that it won't work here since a standard for loop can start with an Expression, which can contain an 'in'.  So this would require arbitrary lookahead to see if there are any ';''s coming or not.

Arbitrary lookahead is no problem for D (it has to do this anyway, a lot, and this is a good thing as it's more important for a language to be comprehensible to the user, and minimise redundant information, than it is for it to be simple to parse), and is makes logical sense, as it iterates over all "a" which are in "aggregate". For example:

    where (uint a; a < 100) // Iterate from 0 to 99.
    where (a; a >= 0 && a < 100 && a % 2) // Iterate over every odd value from 0 to 99.
    where (a; a in list1 && a in list2) // Set intersection.
    where (a; a in list1 || a in list2) // Set union.
    where (a; a in list1 && a !in list2) // Set difference.

    auto list3 = where (a; a in list1 && a in list2); // Wee!

This is pretty easy to do terribly, somewhat harder to do usefully, very hard to do intelligently, and incomprehensibly hard to do at peak optimisability in all cases. I'm not proposing it as a D feature, it's very much not in its style and it's something you'd spend twenty years progressively optimising, but it would be fun to have these kinds of advanced data-processing features in a fast language. So long as we're in fantasy-land:

    where (a; 0 <= a < 100)

Ah, that's better. C's idea that logical operations belong on the same continuum as equations is so very, very 60s. And if we're no longer working with such outdated logic:

    where (a; a in (list1 && list2));

Makes sense to me.
1 2
Next ›   Last »