Perl · Course navigation

Perl subroutines, context, and closures

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

On this page
Before you start

Perl subroutines and arrays.

  • Return different results in list and scalar context, build closures, and write a recursive anonymous function.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

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.

Code example

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

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
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

Behind the scenes

How Perl processes this code

Perl compiles the script internally before executing it. strict and warnings help identify mistakes.

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