Re: $1 not "freezing" in an addition
by tobyink (Canon) on Dec 14, 2012 at 12:32 UTC
|
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...
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'
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
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'
| [reply] [Watch: Dir/Any] [d/l] |
|
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
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
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)
| [reply] [Watch: Dir/Any] [d/l] |
|
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.) | [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |
Re: $1 not "freezing" in an addition
by CountZero (Bishop) on Dec 14, 2012 at 17:13 UTC
|
Without having tested this, could it be that $1 being a global variable, is continously overwritten in each next iteration of this recursive subroutine, until the last step where $1 becomes undef due to the regex failing? That undef value then "trickles" back up the recursive subs and gives this error.By "interpolating"/"quoting" the $1 perhaps the acctual value of $1 at that stage in the recursion gets indeed saved somehow. It is always a good idea to immediately save those special variables (such as $_, @_, $1, ...) into a lexical variable to avoid these pitfalls. We tend to forget that these are global variables that are therefore prone to "action at a distance".
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I think the logic is the same as using Foo($1,$2) versus Foo("$1","$2"), it seems its a good habit to quote $1,$2 when you're not doing assignment
| [reply] [Watch: Dir/Any] |
|
f("$1")
for ("$1")
\"$1"
| [reply] [Watch: Dir/Any] [d/l] |
|
|
|
|
Re: $1 not "freezing" in an addition
by bart (Canon) on Dec 14, 2012 at 12:29 UTC
|
Does it work better if you "unreverse" the if condition?
if(s/\(#:(\d+)\)/#/) {
return $1 + distance($_);
}
As I don't have the whole code, it's a bit difficult to test. And it's just a hunch. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |
Re: $1 not "freezing" in an addition
by Anonymous Monk on Dec 14, 2012 at 12:27 UTC
|
$ perl -Mstrict -Mwarnings -l junk
13
$ cat junk
sub butter {
local $_ = 'effect / 7ACV06';
return $1 + junk($_) if s/(\d+)//;
}
sub junk {
local $_ = $_[0];
return $1 if s/(\d+)//;
}
print butter();
$
| [reply] [Watch: Dir/Any] [d/l] |
|
ok I added the whole code
PS. Modifying your code, here is a simple version that reproduces the error:
sub butter {
my $_ = shift;
return $1 + butter($_) if s/(\d+)//;
return 0;
}
print butter 'effect / 7ACV06';
| [reply] [Watch: Dir/Any] [d/l] |
|
update: after tobyinks 3rd update I see that order of operations plays a role, I still think you should perlbug it, because at least the perlop, and warnings, need to warn more about this ...
Documenting Auto-increment and Auto-decrement is great, but needs to warn about MORE, like this case, or foo($1,$2) <c>foo("$1","$2")
Even B::Lint doesn't warn about it
B::Concise shows no difference between the two
Even the mighty perlcritic doesn't warn about undefined behaviour
But I actually knew about this and forgot
warnings/lint/critic need to recognize and warn me cause I'll probably forget :)
It ought to be easy to recognize an unquoted $1 as a function argument and issue a warning , at least for perlcritic -- if that concise output is any indication, lint might be slightly harder
Maybe perltrap/Common Perl Pitfalls could also use some updating
update: I see, while that doesn't generate a warning, it produces 12 -- good job, you earned some nectar :)
you should definitely perlbug that, and point out that 7 + 6 is 13 and not 12
This part below written before your butterjunkeffect update, on the approach to solving this problem
Ick :) Um, do you really want to find the cause, or do you want a better way to write it?
I'm not inclined to try debugging a program that hacks a tree as a string, I'm inclined to create a tree first (say an array of array or Tree ) and then navigate (calculate distance)
update: tobyink is probably right as to cause
Those rosalind newick trees could be drawn with ascii Dumping trees to a console.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
|
|
Re: $1 not "freezing" in an addition (about rosalind)
by Anonymous Monk on Dec 24, 2012 at 04:31 UTC
|
This is a question about rosalind, specifically the notation of the question, is there a guide to the notation? What is \ldots ?
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
If you turn on javascript you will see the end results instead of the raw latex
| [reply] [Watch: Dir/Any] |