Arrays#

An array is an ordered, integer-indexed sequence of scalars. The sigil is @ for the whole array and for slices (which are lists); $ for a single element (which is one scalar). The leading sigil tells you the result type, not the container.

my @days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri');

@days             # the whole array (5 elements)
$days[0]          # the first element — a single scalar
@days[1, 3]       # a slice — a list of two scalars
$#days            # the last valid index — here, 4
scalar @days      # the length — here, 5

Indices start at 0. Negative indices count from the end ($days[-1] is 'Fri').

Length: $#arr vs scalar @arr#

Two related quantities, used for different jobs:

$#days            # last valid index            — 4
scalar @days      # number of elements          — 5
$#days + 1        # equivalent to scalar @days

Assigning to $#arr resizes the array. Shrinking destroys the elements past the new end; growing fills them with undef:

my @a = (1, 2, 3, 4, 5);
$#a = 2;             # @a is now (1, 2, 3)         — length 3
$#a = 5;             # @a is now (1, 2, 3, undef, undef, undef)
$#a = -1;            # @a is now ()                — empty

The @a = () form is the idiomatic empty-array assignment; $#a = -1 is equivalent and occasionally clearer when the intent is ”shrink to nothing without changing the variable’s identity“.

Element access#

my $first = $days[0];            # 'Mon'
my $last  = $days[-1];           # 'Fri'
my $last  = $days[$#days];        # same; redundant but explicit
$days[2]  = 'Wednesday';         # set
$days[10] = 'Sunday';            # auto-extends; $days[5..9] become undef

Reading past the end returns undef (no warning — out-of-range read is a non-event). Writing past the end grows the array; the intervening slots become undef.

my @a;
$a[5] = 'fifth';
# @a is now (undef, undef, undef, undef, undef, 'fifth')
print scalar @a;                  # 6

Reading a negative index further back than the start is a fatal error:

my @a = (1, 2, 3);
$a[-10];                          # FATAL: Modification of non-creatable
                                  #        array value attempted

The whole-array operations#

push    @arr, $value;             # append at end           — returns new length
my $v = pop  @arr;                # remove from end         — returns removed element
my $v = shift @arr;               # remove from start       — returns removed element
unshift @arr, $value;             # prepend                 — returns new length

splice  @arr, $offset, $len, @replacement;   # general remove/insert

shift and unshift are O(n) because they renumber every remaining element; push and pop are O(1) amortised. For queue-shaped workloads where this matters, prefer push/shift on the short end or use a deque from List::Util (which has reductions, not a deque).

splice is the swiss-army knife: remove a stretch of elements, insert a stretch in their place, return what was removed:

my @arr = (1, 2, 3, 4, 5);
my @gone = splice @arr, 1, 2;            # @gone = (2, 3); @arr = (1, 4, 5)
splice @arr, 1, 0, ('a', 'b');            # insert at offset 1, no removal
                                          # @arr = (1, 'a', 'b', 4, 5)

A negative $offset counts from the end. A negative $len keeps that many trailing elements:

splice @arr, -1;                  # remove just the last element
splice @arr, 0, -2;               # remove all but the last two

Slices#

A slice extracts (or assigns to) several elements at once. Indices come from a list:

my @arr = ('a', 'b', 'c', 'd', 'e');

my @subset = @arr[0, 2, 4];          # ('a', 'c', 'e')
my @middle = @arr[1..3];             # ('b', 'c', 'd')
my @last3  = @arr[-3..-1];           # ('c', 'd', 'e')
my @rev    = @arr[reverse 0..$#arr];  # ('e', 'd', 'c', 'b', 'a')

@arr[1, 3] = ('B', 'D');              # set positions 1 and 3
@arr[1, 3] = @arr[3, 1];              # swap two elements (no temp)

A key/value slice with % returns index/value pairs (Perl 5.20+):

my @arr = ('a', 'b', 'c', 'd', 'e');
my %picked = %arr[1, 3];             # (1 => 'b', 3 => 'd')

This is the right shape when you need both the index and the value of selected elements. See subscript for the twelve-way matrix of slice forms.

List context vs scalar context#

An array in list context yields its elements; in scalar context, it yields its length:

my @arr = (10, 20, 30);

my @copy   = @arr;                # list context — three elements
my $length = @arr;                # scalar context — 3
print "got @arr\n";               # list context inside ""    — "got 10 20 30"
print "got " . @arr . "\n";       # scalar context (concat)   — "got 3"

The distinction is the most common Perl trap. Two rules of thumb:

  • Inside double-quoted strings, arrays interpolate as their elements joined by $" (a single space by default). See interpolation.

  • In a numeric or string operator context (+, ., comparison), an array yields its length.

Force scalar context with scalar when the expression position is otherwise ambiguous:

print "got " . scalar(@arr) . " items\n";    # "got 3 items"
print "got @arr items\n";                    # "got 10 20 30 items" — different!

List assignment and the count idiom#

Assigning a list to an array makes the array exactly that long:

my @arr = (1, 2, 3);
@arr = (10, 20);                  # @arr is now (10, 20)
@arr = ();                        # @arr is now empty

Assigning a list to a list of scalars unpacks (any extras are discarded; missing ones become undef):

my ($x, $y, $z) = (1, 2, 3);              # 1, 2, 3
my ($x, $y, $z) = (1, 2);                 # 1, 2, undef
my ($x, $y)     = (1, 2, 3);              # 1, 2 — '3' silently dropped

A list assignment in scalar context returns the number of elements on the right side. This is the source of the count idiom:

my $count = () = $string =~ /\d+/g;
# $string =~ /\d+/g  — match in list context, returns all matches
# () = LIST          — list assignment to empty list
# $count = SCALAR    — that assignment in scalar context = count of RHS

The () = LIST middle is the trick. Without it, $count = $s =~ /\d+/g gives a boolean instead of a count.

Array references#

Arrays are not first-class values in the sense that they can be passed by name through a chain of function calls without flattening into a list. To pass an array around without flattening, take a reference to it:

my @items = (1, 2, 3);

my $aref = \@items;               # reference to the existing @items
my $anon = [1, 2, 3];             # anonymous array reference

$aref->[0]                         # access through the arrow — 1
@{$aref}                           # fully bracketed deref — list (1, 2, 3)
@$aref                             # unbracketed deref     — same as above
$#{$aref}                          # last index of dereffed array
scalar @$aref                      # length

See references for the full picture.

Real example: building a histogram of word lengths#

my @words = qw(the quick brown fox jumps over the lazy dog);

my @hist;
$hist[length $_]++ for @words;

for my $len (1 .. $#hist) {
    next unless $hist[$len];
    printf "%2d-letter words: %d\n", $len, $hist[$len];
}
# 3-letter words: 4
# 4-letter words: 2
# 5-letter words: 3

Note $hist[length $_]++ autovivifies (creates) the slot when it’s the first word of that length. Reading from $hist[7] later returns undef, which ++ happily increments — but next unless $hist[$len] skips the gap before printing.

See also#