www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.algorithm missing for_each?

reply Jens Mueller <jens.k.mueller gmx.de> writes:
Dear lovely D community,

recently I refactored some code into component style (see Component
Programming in D by Walter
http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321)

It looks about like this

someInputRange
.filter!()
.map!()

Next I want to discard all elements but perform some last operation on
each element. The problem is that map forces me to pass a function that
returns. Of course I could return a fake value. But that doesn't look
proper. Another option is to iterate using the foreach loop. Does not
look better either, does it?

This makes me believe that std.algorithm misses an algorithm. The
for_each algorithm (see for_each in STL
http://www.cplusplus.com/reference/algorithm/for_each/).

To rephrase the problem more general (because maybe I'm just not seeing
how to fit the pieces together): How do you perform some arbitrary
operation on the elements of a range?

myRange
.each!((e)
{
	e.someOperation();
});

Jens
Feb 05 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jens Mueller:

 Another option is to iterate using the foreach loop. Does not 
 look better either, does it?

 ...

 myRange
 .each!((e)
 {
 	e.someOperation();
 });
Adding something like each() has being proposed, but so far no one has implemented it for Phobos. But the need of each() is not strong, there are more important functions to add, like a sum() (being implemented by Andrei), and more. Using foreach() is not bad, it shows clearly to the reader that the code is doing something imperative. So it breaks the style of the UFCS chains, but such breakage reflects the semantic breakage. And the syntax of the each() you have written is a little less readable than a foreach (unless the usage of jQuery and the like has trained your eyes to not see them). One good thing of each() is that it at the end of the chain, while you need to put foreach at its start. This is not handy and it's enough to ask for a each() in Phobos. To improve the syntax of each() and make similar imperative procedures look more like language constructs, some persons have suggested an alternative syntax sugar for functions that have a delegate as last argument: myRange .each { e.someOperation; }; Bye, bearophile
Feb 05 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
 myRange
 .each {
     e.someOperation;
 };
Possibly better: myRange .each (e) { e.someOperation; }; Bye, bearophile
Feb 05 2014
parent "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 5 February 2014 at 11:21:12 UTC, bearophile wrote:
 myRange
 .each {
    e.someOperation;
 };
Possibly better: myRange .each (e) { e.someOperation; }; Bye, bearophile
I is unclear from the syntax whether `(e)` is a the argument list and `{...}` is a block lambda or the property syntax is used and `(e) {...}` is a regular lambda. How about using the Ruby style of blocks: myRange .each do(e) { e.someOperation; }; This will also give a nice syntax when passing named functions: myRange.each do writeln; Alternatively, we can fuse the delegate definition with the higher order function's call syntax like this: myRange .each (auto e) { e.someOperation; }; Here, `(auto e)` *is* the argument list of `each`, but it's clear that `e` is not an argument passed to `each` because it uses the declaration syntax rather than being an expression. Actual arguments could be passed in the same list: forEachInRange(auto i, 0, 10) { ... } This, ofcourse, will require some special syntax for declaring the higher order function.
Feb 05 2014
prev sibling parent reply "Meta" <jared771 gmail.com> writes:
On Wednesday, 5 February 2014 at 11:17:48 UTC, bearophile wrote:
 Adding something like each() has being proposed, but so far no 
 one has implemented it for Phobos.
I implemented it, but the newsgroup reaction was somewhat opposed to it.
Feb 05 2014
parent reply Jens Mueller <jens.k.mueller gmx.de> writes:
Meta wrote:
 On Wednesday, 5 February 2014 at 11:17:48 UTC, bearophile wrote:
Adding something like each() has being proposed, but so far no one
has implemented it for Phobos.
I implemented it, but the newsgroup reaction was somewhat opposed to it.
Can you point me to that forum thread? I'd like to see the arguments raised. Jens
Feb 05 2014
parent reply "Meta" <jared771 gmail.com> writes:
On Wednesday, 5 February 2014 at 13:32:28 UTC, Jens Mueller wrote:
 Meta wrote:
 On Wednesday, 5 February 2014 at 11:17:48 UTC, bearophile 
 wrote:
Adding something like each() has being proposed, but so far 
no one
has implemented it for Phobos.
I implemented it, but the newsgroup reaction was somewhat opposed to it.
Can you point me to that forum thread? I'd like to see the arguments raised. Jens
http://forum.dlang.org/thread/ovbjcmogezbvsxrwfcol forum.dlang.org
Feb 05 2014
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/5/14, 5:36 AM, Meta wrote:
 On Wednesday, 5 February 2014 at 13:32:28 UTC, Jens Mueller wrote:
 Meta wrote:
 On Wednesday, 5 February 2014 at 11:17:48 UTC, bearophile wrote:
Adding something like each() has being proposed, but so far >no one
has implemented it for Phobos.
I implemented it, but the newsgroup reaction was somewhat opposed to it.
Can you point me to that forum thread? I'd like to see the arguments raised. Jens
http://forum.dlang.org/thread/ovbjcmogezbvsxrwfcol forum.dlang.org
Also relevant: http://programmers.stackexchange.com/questions/170939/how-to-make-the-switch-to-c11 Andrei
Feb 05 2014
parent "Meta" <jared771 gmail.com> writes:
On Wednesday, 5 February 2014 at 18:38:04 UTC, Andrei 
Alexandrescu wrote:
 On 2/5/14, 5:36 AM, Meta wrote:
 On Wednesday, 5 February 2014 at 13:32:28 UTC, Jens Mueller 
 wrote:
 Meta wrote:
 On Wednesday, 5 February 2014 at 11:17:48 UTC, bearophile 
 wrote:
Adding something like each() has being proposed, but so far
no one
has implemented it for Phobos.
I implemented it, but the newsgroup reaction was somewhat opposed to it.
Can you point me to that forum thread? I'd like to see the arguments raised. Jens
http://forum.dlang.org/thread/ovbjcmogezbvsxrwfcol forum.dlang.org
Also relevant: http://programmers.stackexchange.com/questions/170939/how-to-make-the-switch-to-c11 Andrei
The major problem (i.e., syntactical) of for_each in C++ don't apply in D due to UFCS and how ranges are implemented.
Feb 05 2014
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 5 February 2014 at 10:03:52 UTC, Jens Mueller wrote:
 Dear lovely D community,

 recently I refactored some code into component style (see 
 Component
 Programming in D by Walter
 http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321)

 It looks about like this

 someInputRange
 .filter!()
 .map!()

 Next I want to discard all elements but perform some last 
 operation on
 each element. The problem is that map forces me to pass a 
 function that
 returns. Of course I could return a fake value. But that 
 doesn't look
 proper. Another option is to iterate using the foreach loop. 
 Does not
 look better either, does it?

 This makes me believe that std.algorithm misses an algorithm. 
 The
 for_each algorithm (see for_each in STL
 http://www.cplusplus.com/reference/algorithm/for_each/).

 To rephrase the problem more general (because maybe I'm just 
 not seeing
 how to fit the pieces together): How do you perform some 
 arbitrary
 operation on the elements of a range?

 myRange
 .each!((e)
 {
 	e.someOperation();
 });

 Jens
Consuming an input range in the functional style should be done by std.algorithm.copy (passing an output range) or by simply passing the input range to the consumer algorithm. Sometimes this isn't possible, which means reverting to foreach, i.e. a mix of functional and imperative style. This is the equivalent of STL's `for_each`. I think adding an `each` function to Phobos is problematic. It is syntax sugar for foreach, but with the downside of obfuscating what is essentially imperative code. IMO, imperative code should look like imperative code, which with foreach is by no means ugly.
Feb 05 2014
parent reply Jens Mueller <jens.k.mueller gmx.de> writes:
Jakob Ovrum wrote:
 On Wednesday, 5 February 2014 at 10:03:52 UTC, Jens Mueller wrote:
Dear lovely D community,

recently I refactored some code into component style (see
Component
Programming in D by Walter
http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321)

It looks about like this

someInputRange
.filter!()
.map!()

Next I want to discard all elements but perform some last
operation on
each element. The problem is that map forces me to pass a function
that
returns. Of course I could return a fake value. But that doesn't
look
proper. Another option is to iterate using the foreach loop. Does
not
look better either, does it?

This makes me believe that std.algorithm misses an algorithm. The
for_each algorithm (see for_each in STL
http://www.cplusplus.com/reference/algorithm/for_each/).

To rephrase the problem more general (because maybe I'm just not
seeing
how to fit the pieces together): How do you perform some arbitrary
operation on the elements of a range?

myRange
.each!((e)
{
	e.someOperation();
});

Jens
Consuming an input range in the functional style should be done by std.algorithm.copy (passing an output range) or by simply passing the input range to the consumer algorithm. Sometimes this isn't possible, which means reverting to foreach, i.e. a mix of functional and imperative style. This is the equivalent of STL's `for_each`. I think adding an `each` function to Phobos is problematic. It is syntax sugar for foreach, but with the downside of obfuscating what is essentially imperative code. IMO, imperative code should look like imperative code, which with foreach is by no means ugly.
Rephrasing your words to get a clear image for myself: You argue that foreach (e; someInputRange .filter!() .map!()) e.someOperation(); is not ugly and the way to go. And someInputRange .filter!() .map!() .each!(); is bad because it mixes two styles. Jens
Feb 05 2014
parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 5 February 2014 at 12:08:11 UTC, Jens Mueller wrote:
 Jakob Ovrum wrote:
 On Wednesday, 5 February 2014 at 10:03:52 UTC, Jens Mueller 
 wrote:
Dear lovely D community,

recently I refactored some code into component style (see
Component
Programming in D by Walter
http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321)

It looks about like this

someInputRange
.filter!()
.map!()

Next I want to discard all elements but perform some last
operation on
each element. The problem is that map forces me to pass a 
function
that
returns. Of course I could return a fake value. But that 
doesn't
look
proper. Another option is to iterate using the foreach loop. 
Does
not
look better either, does it?

This makes me believe that std.algorithm misses an algorithm. 
The
for_each algorithm (see for_each in STL
http://www.cplusplus.com/reference/algorithm/for_each/).

To rephrase the problem more general (because maybe I'm just 
not
seeing
how to fit the pieces together): How do you perform some 
arbitrary
operation on the elements of a range?

myRange
.each!((e)
{
	e.someOperation();
});

Jens
Consuming an input range in the functional style should be done by std.algorithm.copy (passing an output range) or by simply passing the input range to the consumer algorithm. Sometimes this isn't possible, which means reverting to foreach, i.e. a mix of functional and imperative style. This is the equivalent of STL's `for_each`. I think adding an `each` function to Phobos is problematic. It is syntax sugar for foreach, but with the downside of obfuscating what is essentially imperative code. IMO, imperative code should look like imperative code, which with foreach is by no means ugly.
Rephrasing your words to get a clear image for myself: You argue that foreach (e; someInputRange .filter!() .map!()) e.someOperation(); is not ugly and the way to go. And someInputRange .filter!() .map!() .each!(); is bad because it mixes two styles. Jens
Your `foreach` is ugly because you need to look closely to see the boundary between the functional chain and the iterative execution. In this case, adding a block makes things much more elegant: foreach (e; someInputRange .filter!() .map!()) { e.someOperation(); }
Feb 05 2014
parent "Meta" <jared771 gmail.com> writes:
On Wednesday, 5 February 2014 at 12:18:15 UTC, Idan Arye wrote:
 Your `foreach` is ugly because you need to look closely to see 
 the boundary between the functional chain and the iterative 
 execution.

 In this case, adding a block makes things much more elegant:


     foreach (e; someInputRange
                .filter!()
                .map!())
     {
             e.someOperation();
     }
I think that's a bit of an exaggeration. A hypothetical each() function would almost always be at the end of a UFCS chain, making it clear that it's iterating and consuming the result. This is no worse than putting .array at the end of a chain to force evaluation (and is actually much clearer about intent).
Feb 05 2014