www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.algorithm.map with side-effects

reply Joseph Rushton Wakeling via Digitalmars-d-learn writes:
Here's a little experiment I was trying out earlier today in order to try and 
convert foreach-style code to using UFCS of ranges:

//////////////////////////////////////////////
import std.algorithm, std.range, std.stdio;

void main()
{
     size_t s = 0;

     void essify(size_t n)
     {
         writeln("n = ", n);
         ++s;
     }

     auto filteredRange = iota(0, 10).filter!(a => (a % 2));

     filteredRange.map!(a => essify(a));

     writeln(s);

     foreach (n; filteredRange)
     {
         essify(n);
     }

     writeln(s);
}
//////////////////////////////////////////////

I'd assumed the two uses of filteredRange would produce equivalent results, but 
in fact the transformation using map does nothing -- the writeln statement 
inside the essify() function never gets triggered, suggesting the function body 
is never executed.

Can anyone advise why, and whether there's a nice range iteration option to 
ensure that this function gets called using each element of the filteredRange?
Dec 05 2014
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Joseph Rushton Wakeling:

 Can anyone advise why,
map is lazy, like most other ranges.
 and whether there's a nice range iteration option to ensure 
 that this function gets called using each element of the 
 filteredRange?
Lazy higher order functions like map/filter should be used only with pure functions. There are bugs/troubles in using them on impure code. There was a proposal for a "each" function to terminate a range chain with something effectful, but I think it has gone nowhere. This means you have to use a foreach on a range. Bye, bearophile
Dec 05 2014
next sibling parent reply Joseph Rushton Wakeling via Digitalmars-d-learn writes:
On 06/12/14 00:58, bearophile via Digitalmars-d-learn wrote:
 Joseph Rushton Wakeling:

 Can anyone advise why,
map is lazy, like most other ranges.
Ah, I see. That function would only be called on consumption of the results of the map.
 Lazy higher order functions like map/filter should be used only with pure
 functions. There are bugs/troubles in using them on impure code.
Yes, I did wonder about that. I'll post up the actual code tomorrow -- I was having some fun playing with one of the metrics in my Dgraph library and trying to see to what extent I could simplify it (reading-wise) with a range-based approach.
 There was a proposal for a "each" function to terminate a range chain with
 something effectful, but I think it has gone nowhere. This means you have to
use
 a foreach on a range.
Yes, I remember you requesting that. Were there ever any PRs, or was it just spec?
Dec 05 2014
parent "bearophile" <bearophileHUGS lycos.com> writes:
Joseph Rushton Wakeling:

 Yes, I remember you requesting that.  Were there ever any PRs, 
 or was it just spec?
I think two different persons implemented something like "each". A similar function is used very commonly in F#, where it's named "iter": http://msdn.microsoft.com/en-us/library/ee340469.aspx Bye, bearophile
Dec 05 2014
prev sibling parent "evenex" <vlevenfeld gmail.com> writes:
map won't actually compute anything until you start asking for
individual elements with front, back, or opIndex.

Personally I like to use something like

   ref transform (alias action, R, T...)(ref R range, T addl_args)
     {
       range = action (range, addl_args);
       return range;
     }

to do mutation in the middle of UFCS chains. Its more flexible
and more obvious than an impure map.

     range.callchain.array.transform!(x => 
x.map!whatever).filter.etc
Dec 05 2014