Perl using a sub call as argument to another sub - unexpected context
I am using a sub call as an argument to another sub. Example code:
test(isInString(), 'second parameter', 'third parameter');
sub test {
my ($boolean, $second, $third) = @_;
print "boolean: $boolean\n second: $second\n third: $third\n";
}
sub isInString {
my $searchFor = 'a';
my $searchIn = 'bcd';
return ($searchFor && $searchIn && ($searchIn =~ $searchFor));
}
In the example above, I would expect the return statement in "isInString" to evaluate to false ('' or undef or whatever that may be in perl), and this would be passed into "Test" as parameter #1, sou you'd essentially have
Test(undef, 'second parameter', 'third parameter');
That is not what happens though. isInString returns an empty array, and essentially what you get is
Test('second parameter', 'third parameter');
returning from isInString in the perl debugger gives:
list context return from main::isInString: empty array
I'm assuming this is a perl c开发者_Python百科ontext thing, I can assign to a scalar variable first and it works fine:
my $bool = isInString();
Test($bool, 'second parameter', 'third parameter');
debugger gives - scalar context return from main::isInString: ''
EDIT I removed all parens with the same result:
sub isInString {
my $searchFor = 'a';
my $searchIn = 'bcd';
return $searchFor && $searchIn && $searchIn =~ $searchFor;
}
Debugger still gives:
list context return from main::isInString: empty array
Can someone please explain why it is returning a list context/empty array in this scenario?
Your return
is evaluated in list context because the function is called in list context. The boolean operators (like &&
) do not force scalar context.
To quote perlop on &&
:
Scalar or list context propagates down to the right operand if it is evaluated.
Since the match operator returns an empty list on failure in list context, that's what you get. If you want to force scalar context, use the scalar
operator, either in your return
statement, or when you call the function.
Also, the idea that parentheses force list context (as suggested by some of the other answers) is a common misconception. They don't. List context is the default; anything that could be list context is list context. That's why there's no list
operator that corresponds to scalar
. The one place where parens "force" list context is with the repetition operator. 'a' x 3
means 'aaa'
, but ('a') x 3
means ('a', 'a', 'a')
. But that's not actually forcing list context, it's just a special case for the x
operator.
If you want to force a "boolean context" you can use the double-negative "operator" : !!
, like so:
test( !!isInString(), 'second parameter', 'third parameter' );
By calling it in a list of parameters, you've forced it into a "list context". You can force it back with something like this. One bang (!
) evaluates it in a boolean context and makes it either 1 or ''
. But since you aren't testing for the complement, but the value itself, putting the other bang in there gets you back the value you wanted.
However, since it's pretty clear from the function body that you are returning a boolean value, and you're calling it in a list context (a list of parameters is a list context), you might want to force a defined value there. Like so:
return ($searchFor && $searchIn && $searchIn =~ $searchFor) || 0;
Any undef
will result in a false value, and it will jump to the or and return the 0, which a list context will not ignore as it can an undef
.
Not related to your question but index()
is more suitable for your isInString()
function.
index( $searchIn, $searchFor );
精彩评论