Sean Sadykoff Sean Sadykoff - 1 month ago 5
Perl Question

Why doesn't `return $foo if /pattern/;` return $foo even though $foo matches pattern?

I'm using a tool that was published in a Science article but it is giving me a lot of trouble because I am not that familiar with Perl.

The code contains:

return $equa if /\@BOUNDARY/;


I believe that the code is supposed to return
$equa
if it contains the text
@BOUNDARY
, but it doesn't do that. Does the provided code have a mistake?

I am thinking of changing it to:

if ($equa =~ /\@BOUNDARY/) {
return $equa;
}


Does this perform the same function?




For reference, the whole function in the original code is:

sub correctBoundaryReac {
my $equa = shift;
print $equa;
return $equa if /\@BOUNDARY/;
my( $left, $arrow, $right ) = ( '', '', '' );
if( $equa =~ /^(<--|<==>|-->) (.+)/ ){
$arrow = $1;
$right = $2;
$left = $right;
$left =~ s/\@\S+/\@BOUNDARY/g;
}
elsif( $equa =~ /(.+) (<--|<==>|-->)$/ ){
$left = $1;
$arrow = $2;
$right = $left;
$right =~ s/\@\S+/\@BOUNDARY/g;
}
else{
die "Don't know how to fix bounadry reaction: $equa\n";
}
return "$left $arrow $right";
}

Answer

As it has been explained, if (/$pattern/) means if ($_ =~ /$pattern/). See General Variables in perlvar. Then the question is – what is in $_ in the code you show?

Many builtins and operators in Perl use $_ as default. But subroutines don't use $_ for anything, as far as I know. The @_ gets the arguments, but it is a completely different variable.

However, $_ is global. So inside of a sub the $_ from the sub's enclosing scope is visible.

use feature 'say';

sub show_it { say "I see: $_" }

for ('a'..'c') {
    show_it();    # prints with a through c
}

If this is intended in the code you show, it means that the sub checks for the pattern '@BOUNDARY' in the variable $_ – but from the scope at which the sub is called. This is not good practice, to say the least, and can easily lead to subtle bugs. Any time the code in the calling scope is changed one has to review the subs, while we aren't warned of that via sub's interface since it doesn't take $_. Besides, this isn't even needed since the calling code can check and condition the call on that.

I would rather believe that what you show is a simple bug, in which case it should be

return $equa if $equa =~ /\@BOUNDARY/;

This would mean that the sub first checks whether input has this already and if it does it just returns it. The rest of the shown code supports this -- it's all about that '@BOUNDARY'.