Jump to page: 1 26  
Page
Thread overview
foreach thoughts
Jan 14, 2014
Manu
Jan 14, 2014
deadalnix
Jan 14, 2014
Jakob Ovrum
Jan 14, 2014
Jakob Ovrum
Jan 14, 2014
Jakob Ovrum
Jan 14, 2014
Manu
Jan 14, 2014
Manu
Jan 14, 2014
Jakob Ovrum
Jan 14, 2014
Timon Gehr
Jan 14, 2014
Meta
Jan 14, 2014
Timon Gehr
Jan 14, 2014
Timon Gehr
Jan 14, 2014
Manu
Jan 15, 2014
Manu
Jan 14, 2014
Manu
Jan 14, 2014
John Colvin
Jan 14, 2014
Manu
Jan 14, 2014
Dicebot
Jan 14, 2014
Dicebot
Jan 14, 2014
David Nadlinger
Jan 14, 2014
Peter Alexander
Jan 14, 2014
monarch_dodra
Jan 15, 2014
Manu
Jan 15, 2014
David Nadlinger
Jan 15, 2014
Manu
Jan 15, 2014
deadalnix
Jan 15, 2014
David Nadlinger
Jan 15, 2014
Manu
Jan 16, 2014
Kagamin
Jan 14, 2014
deadalnix
Jan 15, 2014
Marco Leise
Jan 15, 2014
Manu
Jan 14, 2014
Jacob Carlborg
Jan 14, 2014
Timon Gehr
Jan 14, 2014
Jakob Ovrum
Jan 14, 2014
Dicebot
Jan 14, 2014
Manu
Jan 14, 2014
Dicebot
Jan 14, 2014
bearophile
Jan 14, 2014
Tobias Pankrath
Jan 14, 2014
Timon Gehr
Jan 14, 2014
Daniel Murphy
Jan 15, 2014
Manu
Jan 15, 2014
H. S. Teoh
January 14, 2014
So, the last few days, 2 things have been coming up constantly.

foreach is like, the best thing about D. But I often get caught by 2 little details that result in my having to create a bunch more visual noise.

1. A termination condition (ie, while)

foreach(t; things) iterates each thing, but it's common in traditional for
loops to have an && in the second 'while' term, to add an additional
termination condition.
for(i=0; i<things.length && things[i].someCondition; ++i)

Or with foreach:
foreach(i, t; things)
{
  if(t.someCondition)
    break;
  ...
}

I often feel like I want something like this where I would have the
opportunity to add that additional term I lose with a traditional for loop:
  foreach(i, t; things; t.someCondition)


2. A filter

The other thing is the ability to skip uninteresting elements. This is
typically performed with the first line of the loop testing a condition,
and then continue:
foreach(i, t; things)
{
  if(!t.isInteresting)
    continue;
  ...
}


I'm finding in practise that at least one of these seems to pop up on the vast majority of loops I'm writing. It produces a lot of visual noise, and I'm finding it's quite a distraction from the otherwise relative tidiness of my code.

How have others dealt with this? I wonder if it's worth exploring a 3rd term in the foreach statement?

I've tried to approach the problem with std.algorithm, but I find the
std.algorithm statement to be much more noisy and usually longer when the
loops are sufficiently simple (as they usually are in my case, which is why
the trivial conditions are so distracting by contrast).
I also find that the exclamation mark overload in typical std.algorithm
statements tends to visually obscure the trivial condition I'm wanting to
insert in the first place.
It's also really hard to debug std.algorithm statements, you can't step the
debugger anymore.

Also, I have to import std.algorithm, which then imports the universe... >_<


January 14, 2014
On Tuesday, 14 January 2014 at 08:23:05 UTC, Manu wrote:
> So, the last few days, 2 things have been coming up constantly.
>
> foreach is like, the best thing about D. But I often get caught by 2 little
> details that result in my having to create a bunch more visual noise.
>
> 1. A termination condition (ie, while)
>
> foreach(t; things) iterates each thing, but it's common in traditional for
> loops to have an && in the second 'while' term, to add an additional
> termination condition.
> for(i=0; i<things.length && things[i].someCondition; ++i)
>
> Or with foreach:
> foreach(i, t; things)
> {
>   if(t.someCondition)
>     break;
>   ...
> }
>
> I often feel like I want something like this where I would have the
> opportunity to add that additional term I lose with a traditional for loop:
>   foreach(i, t; things; t.someCondition)
>

until

>
> 2. A filter
>
> The other thing is the ability to skip uninteresting elements. This is
> typically performed with the first line of the loop testing a condition,
> and then continue:
> foreach(i, t; things)
> {
>   if(!t.isInteresting)
>     continue;
>   ...
> }
>
>
> I'm finding in practise that at least one of these seems to pop up on the
> vast majority of loops I'm writing. It produces a lot of visual noise, and
> I'm finding it's quite a distraction from the otherwise relative tidiness
> of my code.
>

filter

> How have others dealt with this? I wonder if it's worth exploring a 3rd
> term in the foreach statement?
>

We have the tool to build ranges that does what you ask for and feed foreach with them.
January 14, 2014
On Tuesday, 14 January 2014 at 08:23:05 UTC, Manu wrote:
> 1. A termination condition (ie, while)
>
> foreach(t; things) iterates each thing, but it's common in traditional for
> loops to have an && in the second 'while' term, to add an additional
> termination condition.
> for(i=0; i<things.length && things[i].someCondition; ++i)
>
> Or with foreach:
> foreach(i, t; things)
> {
>   if(t.someCondition)
>     break;
>   ...
> }

foreach(t; things.until!(t => t.someCondition))
{
}

Unfortunately foreach over a range does not automatically support an index loop variable. We could add something like std.range.enumerate to support this, but I think it's a common enough requirement that a language amendment is warranted (there are some subtleties involved in implementing it though - specifically when combined with automatic tuple expansion).

> 2. A filter
>
> The other thing is the ability to skip uninteresting elements. This is
> typically performed with the first line of the loop testing a condition,
> and then continue:
> foreach(i, t; things)
> {
>   if(!t.isInteresting)
>     continue;
>   ...
> }

foreach(t; things.filter!(t => t.isInteresting))
{
}

Ditto about the index loop variable.

> I've tried to approach the problem with std.algorithm, but I find the
> std.algorithm statement to be much more noisy and usually longer when the
> loops are sufficiently simple (as they usually are in my case, which is why
> the trivial conditions are so distracting by contrast).

The two examples above look a *lot* cleaner and less noisy (declarative!) to me than the imperative approach using if-break or if-continue.

> I also find that the exclamation mark overload in typical std.algorithm
> statements tends to visually obscure the trivial condition I'm wanting to
> insert in the first place.

You'll have to get used to the exclamation mark, otherwise you'll never be able to fully appreciate D's generic programming. I quite like it - I don't think there's anything objectively ugly about it.

> Also, I have to import std.algorithm, which then imports the universe... >_<

This is fixable. We shouldn't reach for language changes to compensate for library deficiencies.
January 14, 2014
On Tuesday, 14 January 2014 at 08:36:53 UTC, Jakob Ovrum wrote:
> You'll have to get used to the exclamation mark, otherwise you'll never be able to fully appreciate D's generic programming. I quite like it - I don't think there's anything objectively ugly about it.

You subjectively think that there is nothing objectively ugly about it? :-) It is objectively ugly because "!" implies a boolean expression, but that is off-topic.

I agree that chaining of filters and sorting rules is a good solution, provided that you have a high level optimizer capable of transforming the chain into something optimal. You basically need some sort of term-rewriting.
January 14, 2014
On 14 January 2014 18:36, Jakob Ovrum <jakobovrum@gmail.com> wrote:

> On Tuesday, 14 January 2014 at 08:23:05 UTC, Manu wrote:
>
>> 1. A termination condition (ie, while)
>>
>> foreach(t; things) iterates each thing, but it's common in traditional for
>> loops to have an && in the second 'while' term, to add an additional
>> termination condition.
>> for(i=0; i<things.length && things[i].someCondition; ++i)
>>
>> Or with foreach:
>> foreach(i, t; things)
>> {
>>   if(t.someCondition)
>>     break;
>>   ...
>> }
>>
>
> foreach(t; things.until!(t => t.someCondition))
> {
> }
>
> Unfortunately foreach over a range does not automatically support an index loop variable. We could add something like std.range.enumerate to support this, but I think it's a common enough requirement that a language amendment is warranted (there are some subtleties involved in implementing it though - specifically when combined with automatic tuple expansion).
>
>
>  2. A filter
>>
>> The other thing is the ability to skip uninteresting elements. This is
>> typically performed with the first line of the loop testing a condition,
>> and then continue:
>> foreach(i, t; things)
>> {
>>   if(!t.isInteresting)
>>     continue;
>>   ...
>> }
>>
>
> foreach(t; things.filter!(t => t.isInteresting))
> {
> }
>
> Ditto about the index loop variable.
>
>
>  I've tried to approach the problem with std.algorithm, but I find the
>> std.algorithm statement to be much more noisy and usually longer when the
>> loops are sufficiently simple (as they usually are in my case, which is
>> why
>> the trivial conditions are so distracting by contrast).
>>
>
> The two examples above look a *lot* cleaner and less noisy (declarative!) to me than the imperative approach using if-break or if-continue.


/agree completely.
This is nice, I didn't think of writing statements like that :)
That's precisely the sort of suggestion I was hoping for. I'll continue
like this.


January 14, 2014
On Tuesday, 14 January 2014 at 08:43:37 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 14 January 2014 at 08:36:53 UTC, Jakob Ovrum wrote:
>> You'll have to get used to the exclamation mark, otherwise you'll never be able to fully appreciate D's generic programming. I quite like it - I don't think there's anything objectively ugly about it.
>
> You subjectively think that there is nothing objectively ugly about it? :-) It is objectively ugly because "!" implies a boolean expression, but that is off-topic.

This argument is stupid. It's the same argument as the famous "Yeah, well, that's just, like, your opinion, man", or the "please put `I think...` in front of all your sentences!" argument.

The burden of proof is on the person who first claims it's deficient. Saying that I can't think of anything objectively wrong with it serves the purpose of inviting Manu to provide some kind of argument, as I can't think of anything *obviously* wrong about it that goes unsaid.

It's common to overload tokens in programming languages, and it's usually only a problem for beginners who aren't used to the particular language's choice of overloads yet (Ruby is good example of a language with rather extreme token reuse) - humans are pretty good at context-sensitive parsing. From a character-by-character perspective it's particularly common, with the bitwise shift operators having nothing to do with comparisons, bitwise xor having nothing to do with exponents etc.

Regardless of whether binary ! is "ugly" or not, it's still better than introducing language features left and right to avoid templates, and it's still better than C++'s template instantiation syntax :)

> I agree that chaining of filters and sorting rules is a good solution, provided that you have a high level optimizer capable of transforming the chain into something optimal. You basically need some sort of term-rewriting.

LDC and GDC are capable of unravelling the (fairly thin) abstraction. All it requires is the ability to inline direct function calls to small functions - the aforementioned compilers always have this capability for templated functions.

DMD is hit and miss, but I think there was a recent improvement to its inliner... luckily this is still the domain of micro-optimization.
January 14, 2014
On 14 January 2014 19:04, Manu <turkeyman@gmail.com> wrote:

> On 14 January 2014 18:36, Jakob Ovrum <jakobovrum@gmail.com> wrote:
>
>> On Tuesday, 14 January 2014 at 08:23:05 UTC, Manu wrote:
>>
>>> 1. A termination condition (ie, while)
>>>
>>> foreach(t; things) iterates each thing, but it's common in traditional
>>> for
>>> loops to have an && in the second 'while' term, to add an additional
>>> termination condition.
>>> for(i=0; i<things.length && things[i].someCondition; ++i)
>>>
>>> Or with foreach:
>>> foreach(i, t; things)
>>> {
>>>   if(t.someCondition)
>>>     break;
>>>   ...
>>> }
>>>
>>
>> foreach(t; things.until!(t => t.someCondition))
>> {
>> }
>>
>> Unfortunately foreach over a range does not automatically support an index loop variable. We could add something like std.range.enumerate to support this, but I think it's a common enough requirement that a language amendment is warranted (there are some subtleties involved in implementing it though - specifically when combined with automatic tuple expansion).
>>
>>
>>  2. A filter
>>>
>>> The other thing is the ability to skip uninteresting elements. This is
>>> typically performed with the first line of the loop testing a condition,
>>> and then continue:
>>> foreach(i, t; things)
>>> {
>>>   if(!t.isInteresting)
>>>     continue;
>>>   ...
>>> }
>>>
>>
>> foreach(t; things.filter!(t => t.isInteresting))
>> {
>> }
>>
>> Ditto about the index loop variable.
>>
>>
>>  I've tried to approach the problem with std.algorithm, but I find the
>>> std.algorithm statement to be much more noisy and usually longer when the
>>> loops are sufficiently simple (as they usually are in my case, which is
>>> why
>>> the trivial conditions are so distracting by contrast).
>>>
>>
>> The two examples above look a *lot* cleaner and less noisy (declarative!) to me than the imperative approach using if-break or if-continue.
>
>
> /agree completely.
> This is nice, I didn't think of writing statements like that :)
> That's precisely the sort of suggestion I was hoping for. I'll continue
> like this.
>

Can anyone comment on the codegen when using these statements? Is it
identical to my reference 'if' statement?
Liberal use of loops like this will probably obliterate unoptimised
performance... :/


January 14, 2014
On Tuesday, 14 January 2014 at 09:06:23 UTC, Jakob Ovrum wrote:

> It's common to overload tokens in programming languages, and it's usually only a problem for beginners who aren't used to the particular language's choice of overloads yet (Ruby is good example of a language with rather extreme token reuse) - humans are pretty good at context-sensitive parsing. From a character-by-character perspective it's particularly common, with the bitwise shift operators having nothing to do with comparisons, bitwise xor having nothing to do with exponents etc.

BTW, I am not implying Manu is a beginner, I suspect he had something more specific in mind when he mentioned the instantiation syntax.
January 14, 2014
On 14 January 2014 19:09, Jakob Ovrum <jakobovrum@gmail.com> wrote:

> On Tuesday, 14 January 2014 at 09:06:23 UTC, Jakob Ovrum wrote:
>
>  It's common to overload tokens in programming languages, and it's usually
>> only a problem for beginners who aren't used to the particular language's choice of overloads yet (Ruby is good example of a language with rather extreme token reuse) - humans are pretty good at context-sensitive parsing. From a character-by-character perspective it's particularly common, with the bitwise shift operators having nothing to do with comparisons, bitwise xor having nothing to do with exponents etc.
>>
>
> BTW, I am not implying Manu is a beginner, I suspect he had something more specific in mind when he mentioned the instantiation syntax.
>

Thank you... I was going to comment, but I restrained myself ;)


January 14, 2014
On 14 January 2014 19:06, Jakob Ovrum <jakobovrum@gmail.com> wrote:

> On Tuesday, 14 January 2014 at 08:43:37 UTC, Ola Fosheim Grøstad wrote:
>
>> On Tuesday, 14 January 2014 at 08:36:53 UTC, Jakob Ovrum wrote:
>>
>>> You'll have to get used to the exclamation mark, otherwise you'll never be able to fully appreciate D's generic programming. I quite like it - I don't think there's anything objectively ugly about it.
>>>
>>
>> You subjectively think that there is nothing objectively ugly about it? :-) It is objectively ugly because "!" implies a boolean expression, but that is off-topic.
>>
>
> This argument is stupid. It's the same argument as the famous "Yeah, well, that's just, like, your opinion, man", or the "please put `I think...` in front of all your sentences!" argument.
>
> The burden of proof is on the person who first claims it's deficient. Saying that I can't think of anything objectively wrong with it serves the purpose of inviting Manu to provide some kind of argument, as I can't think of anything *obviously* wrong about it that goes unsaid.
>
> It's common to overload tokens in programming languages, and it's usually only a problem for beginners who aren't used to the particular language's choice of overloads yet (Ruby is good example of a language with rather extreme token reuse) - humans are pretty good at context-sensitive parsing. From a character-by-character perspective it's particularly common, with the bitwise shift operators having nothing to do with comparisons, bitwise xor having nothing to do with exponents etc.
>
> Regardless of whether binary ! is "ugly" or not, it's still better than introducing language features left and right to avoid templates, and it's still better than C++'s template instantiation syntax :)


Personally, I generally like the '!' syntax, except when it's in conjunction with lambda's where it can kinda ruins the statements a bit.


 I agree that chaining of filters and sorting rules is a good solution,
>> provided that you have a high level optimizer capable of transforming the chain into something optimal. You basically need some sort of term-rewriting.
>>
>
> LDC and GDC are capable of unravelling the (fairly thin) abstraction. All it requires is the ability to inline direct function calls to small functions - the aforementioned compilers always have this capability for templated functions.
>
> DMD is hit and miss, but I think there was a recent improvement to its inliner... luckily this is still the domain of micro-optimization.
>

I'm quite concerned about unoptimised performance too. I know that's unusual, but I'm often left wondering what to do about it.

Imagine, iterating over an array, if I use .filter(lambda) or something, there's now an additional 2 function calls at least per iteration, as opposed to the original zero. If this is idiomatic (I'd like to think it should be since it's fairly tidy), then we commit to a serious performance problem in unoptimised code. Liberal use of .empty and things too is a problem in unoptimised code... :/


« First   ‹ Prev
1 2 3 4 5 6