Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Re: $1 not "freezing" in an addition

by tobyink (Canon)
on Dec 14, 2012 at 12:32 UTC ( [id://1008827]=note: print w/replies, xml ) Need Help??


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'

Replies are listed 'Best First'.
Re^2: $1 not "freezing" in an addition
by tobyink (Canon) on Dec 14, 2012 at 15:08 UTC

    Here's another fun example...

    use 5.010; my $z = 0; sub xxx () { $z = 1; return 2; } sub yyy () { my $zz = $z; $z = 0; return 2 + $zz; } if (xxx + yyy == yyy + xxx) { say "Addition is commutative"; } else { say "The world has gone haywire!"; }

    See addition; commutativity; etc.

    Don't write functions with side-effects. And if you really must write functions with side-effects, be careful using them in expressions.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re^2: $1 not "freezing" in an addition
by ikegami (Patriarch) on Dec 16, 2012 at 14:36 UTC

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

    The example you gave has nothing to do with operand evaluation order and everything to do with operator precedence. Your code is equivalent to print(foo(+bar(), "\n"));, for which operand evaluation order *is* defined.

    If you had used print foo() + bar(), "\n"; (or added the empty prototype, as you mentioned), then you would be relying on undefined operand evaluation order, yet it will give you the following on all machines:

    foo just happened bar just happened 42
Re^2: $1 not "freezing" in an addition
by ikegami (Patriarch) on Dec 16, 2012 at 14:13 UTC

    Perl does not guarantee what order the two operands of a binary operator will be evaluated in.

    Operand evaluation order is documented and thus guaranteed for about half the ops*. And while it's not guaranteed for the other ops, it never changes (left-to-right for all ops (including **) except assignment ops).


    * — Going from memory, operand evaluation order is documented for:

    • && || // and or xor ?:
    • = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= //= x=
    • , => (in scalar context)
    • .. ... (in scalar context)
Re^2: $1 not "freezing" in an addition
by ikegami (Patriarch) on Dec 16, 2012 at 14:44 UTC

    So your problem is that the recursive call to butter is evaluated before $1.

    Quotes around $1 or using a subexpression like (0+$1) coerces Perl into changing the evaluation order,

    Both of those statements are completely false. In all existing builds of Perl, the LHS of addition is always evaluated before it's RHS, so $1 is evaluated first in $1+f(). Keep in mind that $1 evaluates to a scalar, not a string. It's "+" that extracts the string from it, by which point the scalar's value has changed.

    Study the following:

    $_ = 3; print sub :lvalue { print "$_+"; $_ }->() + sub :lvalue { print "10="; ++$_; 10 }->(), "\n";
    3+10=14

    You can clearly see $_ being evaluated before 10 even though the value to which $_ is set by the RHS is used by the addition.

    (Sorry for making a chain of post. I initially assumed your post was correct and that it just had a small problem, so I commented on it before reading on. And then the same thing happened another time. Only after that did I realize the entire post is wrong.)

Re^2: $1 not "freezing" in an addition
by grondilu (Friar) on Dec 14, 2012 at 15:45 UTC

    Very instructive. Thanks.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1008827]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (5)
As of 2024-03-19 08:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found