I encountered this today and thought it prudent to post a Q&A as I couldn't find anything similar.
Feel free to vote-to-close if you find a duplicate of this question.
The following subroutine conditionally return
s output; I consider it "clumsy" because it isn't explicit about what is returned to the caller when the conditional is not satisfied:
sub is_multiple_of_three {
my ( $value ) = @_ ;
return "$value is a multiple of 3"
unless $value % 3;
}
A quick rewrite makes short work of clarifying the (more graceful) subroutine's intended behaviour under all circumstances:
sub is_multiple_of_three {
my ( $value ) = @_ ;
return if $value % 3;
return "$value is a multiple of 3";
}
When calling these both flavours of the subroutine, I was expecting to find some consistency between what both return
in list context:
But alas, the behaviour was slightly unexpected:
use strict;
use warnings;
use Data::Printer;
use feature 'say';
my %subs = (
graceful => sub {
my ( $value ) = @_ ;
return if $value % 3;
return "$value is a multiple of 3";
},
clumsy => sub {
my ( $value ) = @_ ;
return "$value is a multiple of 3"
unless $value % 3;
},
);
for my $name ( keys %subs ) {
my $sub = $subs{$name};
say $name;
my @results = map { $sub->($_) } 1 .. 10;
p @results;
}
Output
graceful
[
[0] "3 is a multiple of 3",
[1] "6 is a multiple of 3",
[2] "9 is a multiple of 3"
]
clumsy
[
[0] 1,
[1] 2,
[2] "3 is a multiple of 3",
[3] 1,
[4] 2,
[5] "6 is a multiple of 3",
[6] 1,
[7] 2,
[8] "9 is a multiple of 3",
[9] 1
]
Question
The "graceful" flavour behaves as expected, but why is the "clumsy" sub returning back integers when the conditional is false?
The behaviour is consistent with what is documented in perldoc perlsub
A
return
statement may be used to exit a subroutine, optionally specifying the returned value, which will be evaluated in the appropriate context (list, scalar, or void) depending on the context of the subroutine call. If you specify no return value, the subroutine returns an empty list in list context, the undefined value in scalar context, or nothing in void context. If you return one or more aggregates (arrays and hashes), these will be flattened together into one large indistinguishable list.If no return is found and if the last statement is an expression, its value is returned. If the last statement is a loop control structure like a
foreach
or awhile
, the returned value is unspecified. The empty sub returns the empty list.
The graceful sub in list context:
True : returns the string "$value is a multiple of 3"
is returned
False : returns an empty list
Which is why there are only three elements in @results
; something is added to the array only when the conditional evaluates to true.
The clumsy sub in list context:
"$value is a multiple of 3"
is returned. No dramas here.return
is encountered, returns the value of the last expression evaluated , $value % 3
So in both cases, the subroutine will return back a value, which is why @results
has ten items in it.
For last-expression purposes, foo if bar
and if (bar) { foo }
are equivalent to bar and foo
, and similarly unless is equivalent to or.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With