Perl subroutines, context, and closures

Return different results in list and scalar context, build closures, and write a recursive anonymous function.

Perl
use strict;
use warnings;

sub minmax {
    my @sorted = sort { $a <=> $b } @_;
    return wantarray ? ($sorted[0], $sorted[-1]) : "$sorted[0]..$sorted[-1]";
}
my ($min, $max) = minmax(4, 9, 2, 7);
my $range = minmax(4, 9, 2, 7);
print "$min $max $range\n";

sub counter {
    my $n = shift // 0;
    return { inc => sub { return ++$n }, get => sub { return $n } };
}
my $c1 = counter();
my $c2 = counter(10);
$c1->{inc}->() for 1 .. 3;
$c2->{inc}->();
print $c1->{get}->(), " ", $c2->{get}->(), "\n";

my $factorial;
$factorial = sub {
    my $n = shift;
    return $n <= 1 ? 1 : $n * $factorial->($n - 1);
};
print $factorial->(5), "\n";

my @list = (3, 1, 2);
my $count = @list;
my ($first) = @list;
print "$count $first\n";
Output
2 9 2..9
3 11
120
3 3

Explanation

Perl subroutines receive their arguments in the array @_ and usually copy them with my. Context is central to Perl: the same expression can behave differently in list and scalar context. A subroutine can ask for its caller's context with wantarray and return a list in one case and a single value in the other. An array in scalar context is its length, while assigned to a list it gives its elements.

A closure is an anonymous subroutine that remembers the my variables that were in scope when it was created. counter() below returns two closures that share one $n, and every call to counter() creates a separate $n. An anonymous subroutine can call itself through a variable declared beforehand.

Explore the dry run
Guided walkthroughReady
1use strict;
2use warnings;
3
4sub minmax {
5 my @sorted = sort { $a <=> $b } @_;
6 return wantarray ? ($sorted[0], $sorted[-1]) : "$sorted[0]..$sorted[-1]";
7}
8my ($min, $max) = minmax(4, 9, 2, 7);
9my $range = minmax(4, 9, 2, 7);
10print "$min $max $range\n";
11
12sub counter {
13 my $n = shift // 0;
14 return { inc => sub { return ++$n }, get => sub { return $n } };
15}
16my $c1 = counter();
17my $c2 = counter(10);
18$c1->{inc}->() for 1 .. 3;
19$c2->{inc}->();
20print $c1->{get}->(), " ", $c2->{get}->(), "\n";
21
22my $factorial;
23$factorial = sub {
24 my $n = shift;
25 return $n <= 1 ? 1 : $n * $factorial->($n - 1);
26};
27print $factorial->(5), "\n";
28
29my @list = (3, 1, 2);
30my $count = @list;
31my ($first) = @list;
32print "$count $first\n";

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
wantarray is true in list context. The subroutine returns a list there and a string in scalar context.—
—
Assigning to a list of variables gives the call list context.—
—
Assigning to one scalar gives the call scalar context.—
—
The same call produced different results.—
2 9 2..9
Two closures that share the private variable $n.—
2 9 2..9
The first counter is incremented three times, the second once.—
2 9 2..9
The counters do not affect each other.—
2 9 2..9
3 11
The anonymous subroutine calls itself through $factorial: 5·4·3·2·1.—
2 9 2..9
3 11
120
An array in scalar context is its length.—
2 9 2..9
3 11
120
my ($first) = @list takes only the first element, the parentheses make it a list assignment.—
2 9 2..9
3 11
120
3 3

Cost and common mistakes

Each call is O(1) except minmax, which sorts in O(n log n).

  • my $first = @list gives the length, not the first element. The parentheses in my ($first) = @list change the context.
  • Declare $factorial before assigning the closure, or the name is not yet visible inside the anonymous subroutine.
  • Modifying @_ elements directly changes the caller's variables because @_ aliases them. Copy with my first.

Try it yourself

What does print scalar(minmax(1, 2)) print?

Reveal answer and reasoning

1..2: scalar() forces scalar context, so minmax returns the string.

Further reading