digitalmars.D.learn - Function called twice
- Jordan Wilson (22/22) Aug 02 2019 Hello,
 - Adam D. Ruppe (10/14) Aug 02 2019 I *think* what's happening here is first it calls map() first
 - Jordan Wilson (5/20) Aug 02 2019 In my real-world case "isEven" is a long running function. I'll
 - Boris Carvajal (9/31) Aug 02 2019 The way map is designed is to call its predicate on each front
 - Simen =?UTF-8?B?S2rDpnLDpXM=?= (13/33) Aug 02 2019 map doesn't memoize its front value
 
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
 Aug 02 2019
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
 Aug 02 2019
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: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// 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
 Aug 02 2019
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;
 Aug 02 2019
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
 Aug 02 2019








 
 
 
 Jordan Wilson <wilsonjord gmail.com> 