http://www.perlmonks.org?node_id=1008827


in reply to $1 not "freezing" in an addition

I've struck out some text below which has become irrelevant as the question has been updated...

What version of Perl? The following seems to work OK on Perl 5.8.9, 5.10.1 and 5.16.0...

use strict; use warnings FATAL => 'all'; use Test::More; sub distance { length $_[0]; } sub answer { $_ = $_[0]; return $1 + distance($_) if s/\(#:(\d+)\)/#/; } is( answer('Life, the universe & everything? (#:3) Huh?'), 42, ); done_testing;

Update: you've posted the whole code now. I get the same error. Another solution (other than 0+$1) is to quote the variable, "$1".

This is indeed strange, but it's not the first weird bug involving magic variables, and probably won't be the last. Best to report it to the p5p bug tracker I think.

Update #2: I found a similar problem a few months ago... Bizarro %+ hash slice bug in Perl 5.10 to 5.14..

Update #3: Now you've added some simplified code and I can see what's going on! That code actually runs more or less how I'd expect it to run.

The problem is that you're expecting that on the first iteration, $1 will be evaluated before the recursive call to butter however that is not necessarily the case. With the exception of the short-circuiting operators (&&, || and so on), Perl does not guarantee what order the two operands of a binary operator will be evaluated in.

For example:

use strict; use warnings FATAL => 'all'; sub foo { print "foo just happened\n"; 40; } sub bar { print "bar just happened\n"; 2; } print foo + bar, "\n";

What happens? You might expect that you'd get output like this:

foo just happened bar just happened 42

The "correct" behaviour here is undefined. As it happens, on my machine the first two lines are reversed.

So your problem is that the recursive call to butter is evaluated before $1. The second call of butter ends up setting $1 to 6, overwriting the existing value 7. Blammo! 6+6 is 12.

Quotes around $1 or using a subexpression like (0+$1) coerces Perl into changing the evaluation order, and (by luck more than anything else) you get the correct answer.

Update 4: the C FAQ (C is a programming language with similar undefined behaviour here) has this related entry.

Update 5: this is very fragile stuff. If you add a prototype of () to the foo function in my example, then it changes the output order (at least it does here). If take away the prototype but call foo with parentheses, like foo() then that has the same effect.

Interesting SoPW post though. Kept me entertained. Much thanks.

perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'