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#
Hashes — when the index is a string, not an integer.
References — passing arrays around without flattening.
Context — the list-vs-scalar story expanded.
Interpolation — how arrays appear inside
"".Subscript and slice operators — the bracket-and-brace family in detail.
Range operator
..— the most common source of slice index lists.push,pop,shift,unshift,splice— the whole-array operations.scalar— force scalar context (length).