Thread overview | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 05, 2014 std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
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 |
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jens Mueller | 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
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | > myRange
> .each {
> e.someOperation;
> };
Possibly better:
myRange
.each (e) {
e.someOperation;
};
Bye,
bearophile
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jens Mueller | 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.
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | 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.
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum | 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
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jens Mueller | 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();
}
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | 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.
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Idan Arye | 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).
|
February 05, 2014 Re: std.algorithm missing for_each? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | 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
|
Copyright © 1999-2021 by the D Language Foundation