www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Function called twice

reply Jordan Wilson <wilsonjord gmail.com> writes:
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
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent Jordan Wilson <wilsonjord gmail.com> writes:
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
Aug 02 2019
prev sibling next sibling parent Boris Carvajal <boris2.9 gmail.com> writes:
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
prev sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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