I have been trying to make a relatively large Perl program that has been working perfectly fine on CentOS for many years to work on Ubuntu and this has become a huge nightmare. CentOS uses Perl built for x86_64-linux-thread-multi and Ubuntu uses the x86_64-linux-gnu-thread-multi build. AFAIK, the interpreter behavior should be the same in both environments when the program invokes the same previous version v5.10.1
. However I have been getting very different behavior, including warnings about given
/when
and smartmatch
being experimental and, most importantly, a set of nasty bugs that are hard to trace and resolve. A particular problem occurs when a given
statement shown below (form 1) matches and calls a function. Then suddenly the value of the switch variable (called $ailtype
) that is never otherwise touched gets erased from memory! If I simply call that function nothing obnoxious happens. So, I replaced the given
/when
usage with a for
statement (form 2) and my question is why does the the problem still occur?! The only form that truly avoids the problem is a simple chain of if
/elsif
s (form 3) and this clearly shows that the problem is with forms 1 and 2 and the perl interpreter being inconsistent and bug-ridden: it does not even produce an "experimental" warning for form 2.
Here is form 1 (original):
print "ailtype is $ailtype \n"; # prints "ailtype is 8"
given ($ailtype) {
when (4) { &parse_mascot}
when (5) { &parse_sequest}
when (8) { &parse_spectrast($ms2_results, $rttemp)}
when (9) { &parse_cnstab($ms2_results, $rttemp)}
default { STDOUT->autoflush(1) and die "ailtype=|$ailtype| unknown.\n"; }
}
print "ailtype is $ailtype \n"; # prints "ailtype is ". $ailtype got destroyed!
The print
s are for debug. I can put them inside the when
block and confirm that $ailtype
gets destroyed after the &parse_spectrast
function call. However, the function does not read or touch $ailtype
at all! (Interestingly, if I go inside the function and print the value of $ailtype
to find exactly where it gets messed up, I see that it occurs within a while
loop parsing the lines of an input file. Printing $ailtype
somehow returns the lines of that file!)
The program consists of several large perl files with many given
/when
statements and re-writing all of them by hand would be tedious. I have to make sure that the alternative form works. So I tried form 2 (suggested here):
print "ailtype is $ailtype \n"; # prints "ailtype is 8"
for ($ailtype) {
/4/ and do { &parse_mascot; last};
/5/ and do { &parse_sequest; last};
/8/ and do { &parse_spectrast($ms2_results, $rttemp); last};
/9/ and do { &parse_cnstab($ms2_results, $rttemp); last};
do { STDOUT->autoflush(1) and die "ailtype=|$ailtype| unknown.\n"; }
}
print "ailtype is $ailtype \n"; # prints "ailtype is ". $ailtype still gets destroyed!
The problem still occurs in this form, and I really don't understand why?! The interpreter no longer warns about experimental given
/when
here (they're still used elsewhere in the code though. I ensured they didn't occur before the problematic block and that didn't help).
Surprising or not, a chain of if
/elsif
s (form 3) works fine:
print "ailtype is $ailtype \n"; # prints "ailtype is 8"
if (4 == $ailtype) {
&parse_mascot;
}
elsif (5 == $ailtype) {
&parse_sequest;
}
elsif (8 == $ailtype) {
&parse_spectrast($ms2_results, $rttemp);
}
elsif (9 == $ailtype) {
&parse_cnstab($ms2_results, $rttemp);
}
else {
STDOUT->autoflush(1) and die "ailtype=|$ailtype| unknown.\n";
}
print "ailtype is $ailtype \n"; # prints "ailtype is 8". It was never changed.
But I thought the powers of Perl were there to save us from having to code all of this. I would be willing to do so if I was sure I had an otherwise reliable interpreter. Changing the version to v5.16.3 (the latest installed on both platforms) only produces new errors regarding declarations, "bareword STDOUT not allowed", etc. Having fought with this bug alone for 10 hours, I am seriously in doubt.
for
aliases $_
for each value in the loop. If the called function changes the value of $_
, the original variable will be changed, as well. The diamond operator used in a while loop changes the global $_
and it becomes undef
on the eof
.
#!/usr/bin/perl
use warnings;
use strict;
sub f {
while (<DATA>) {
warn "read:\t$_";
}
}
my $x = 8;
print "Before:<<$x>>\n";
for ($x) {
&f();
}
print "After:<<$x>>\n";
__DATA__
A
B
Solution:
Insert the following line before the while (<>) {
:
local $_;
This will restore the original value of $_
when leaving the scope of the local
statement.