Re: Function called twice

2019-08-02 Thread Simen Kjærås via Digitalmars-d-learn

On Friday, 2 August 2019 at 21:44:28 UTC, Jordan Wilson wrote:

Hello,

I don't quite understand why isEven is called twice in the 2nd 
example?


auto isEven(int n) {
n.writeln;
return (n % 2) == 0;
}

void main() {

auto z = [1,2,3];

// outputs 1 2 3
z.map!(a => tuple!("number")(a))
 .filter!(a => a.number.isEven)
 .array;

// outputs 1 2 2 3
z.map!(a => tuple!("number","iseven")(a, a.isEven))
 .filter!(a => a.iseven)
 .array;

return;
}


map doesn't memoize its front value 
(https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L604), so every time someone looks at it the mapping function needs to be called again. So in the second case, for each item first filter calls map.front which calls isEven, then if it matches the filter (so only the 2), array calls filter.front, which calls map.front, which calls isEven.


You can avoid this by eagerly iterating to an array:

z.map!(a => tuple!("number","iseven")(a, a.isEven))
.array // new!
.filter!(a => a.iseven)
.array;

I was also gonna suggest using std.functional.memoize to memoize 
the function in map, but apparently it doesn't like lambdas 
(https://issues.dlang.org/show_bug.cgi?id=20099).


--
  Simen


Re: Function called twice

2019-08-02 Thread Boris Carvajal via Digitalmars-d-learn

On Friday, 2 August 2019 at 21:44:28 UTC, Jordan Wilson wrote:

Hello,

I don't quite understand why isEven is called twice in the 2nd 
example?


auto isEven(int n) {
n.writeln;
return (n % 2) == 0;
}

void main() {

auto z = [1,2,3];

// outputs 1 2 3
z.map!(a => tuple!("number")(a))
 .filter!(a => a.number.isEven)
 .array;

// outputs 1 2 2 3
z.map!(a => tuple!("number","iseven")(a, a.isEven))
 .filter!(a => a.iseven)
 .array;

return;
}

Thanks,

Jordan


The way map is designed is to call its predicate on each front 
call and filter calls it twice with the number 2.

https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L604

You can use "cache" to avoid the double front call on any range.

z.map!(a => tuple!("number","iseven")(a, a.isEven))
 .cache
 .filter!(a => a.iseven)
 .array;



Re: Function called twice

2019-08-02 Thread Jordan Wilson via Digitalmars-d-learn

On Friday, 2 August 2019 at 22:35:53 UTC, Adam D. Ruppe wrote:

On Friday, 2 August 2019 at 21:44:28 UTC, Jordan Wilson wrote:

// outputs 1 2 2 3
z.map!(a => tuple!("number","iseven")(a, a.isEven))
 .filter!(a => a.iseven)
 .array;


I *think* what's happening here is first it calls map() first 
going into the filter... then it gets called again when it is 
being evaluated for the array.


So like basically consider if you had:

if(filter(a())
   array ~= a();


that kind of thing. I think anyway, I'm not entirely sure but 
it fits what I can see happening here.


but idk why it is actually doing this in the phobos 
implementation


In my real-world case "isEven" is a long running function. I'll 
put an .array after the map and see if it makes a difference.


Thanks,

Jordan


Re: Function called twice

2019-08-02 Thread Adam D. Ruppe via Digitalmars-d-learn

On Friday, 2 August 2019 at 21:44:28 UTC, Jordan Wilson wrote:

// outputs 1 2 2 3
z.map!(a => tuple!("number","iseven")(a, a.isEven))
 .filter!(a => a.iseven)
 .array;


I *think* what's happening here is first it calls map() first 
going into the filter... then it gets called again when it is 
being evaluated for the array.


So like basically consider if you had:

if(filter(a())
   array ~= a();


that kind of thing. I think anyway, I'm not entirely sure but it 
fits what I can see happening here.


but idk why it is actually doing this in the phobos implementation