Perl Part I · Module 5 · Arrays and Lists
Array Slices
Having split a line into fields, you usually want three of its eight columns. Reading them one at a time works and takes three statements; a slice does it in one expression by taking a list of positions instead of a single one. The construct is also where the sigil rule from Chapter 5.1 finally pays off, because a slice is the first place in this track where an at sign in front of a subscript is correct rather than a mistake — it produces several values, so it takes the several-values sigil. Two properties make a slice more useful than it first appears: the results come back in the order you asked for rather than in array order, and a slice can sit on the left of an assignment, which turns swapping two elements into a single line with no temporary variable.
Foundation13 min readPerlPart IFundamentalsArraysParsing
Part I · Module 5 · Chapter 5.3 · Array Slices
1. Several Positions at Once
use strict;
use warnings;
my @row = ("alu", "smoke", "PASS", "184", "7");
my @picked = @row[0, 2];
my @range = @row[1 .. 3];
my @tail = @row[-2, -1];
print "whole : @row\n";
print "\@row[0,2] : @picked\n";
print "\@row[1..3] : @range\n";
print "\@row[-2,-1] : @tail\n";whole : alu smoke PASS 184 7
@row[0,2] : alu PASS
@row[1..3] : smoke PASS 184
@row[-2,-1] : 184 7A slice is a subscript containing a list of indexes rather than one. It returns one element per index.
Three things that follow from that:
- The sigil is
@. A slice produces several values, so it takes the several-values sigil — the rule Chapter 5.1 §2 stated. This is the first construct in the track where@in front of a subscript is correct. - The index list is an ordinary list.
0, 2is two values;1 .. 3is the range operator from Chapter 3.10 producing three. Anything that makes a list of numbers works. - Negative indexes work, exactly as in Chapter 5.2 §1.
@row[-2, -1]is "the last two".
Which retroactively explains the warning
Chapter 5.1 §2 mentioned that @row[0] warns. Now the reason is visible:
use strict;
use warnings;
my @row = ("alu", "smoke");
my $value = @row[0];
print "value: $value\n";value: aluScalar value @row[0] better written as $row[0] at one-index-slice.pl line 6.It works — $value is alu. It warns because a slice of one index produces a one-element list, which then collapses to that one value in a scalar assignment, and writing $row[0] says what you meant directly.
The rule stays simple: one index wants $, several want @.
2. The Order Is Yours, and Indexes May Repeat
This is the property that makes a slice a selector rather than just a range reader.
use strict;
use warnings;
my @row = ("a", "b", "c");
print "array order : @row[0, 1, 2]\n";
print "reordered : @row[2, 0, 1]\n";
print "repeated : @row[0, 0, 0]\n";
print "just one twice: @row[1, 1]\n";array order : a b c
reordered : c a b
repeated : a a a
just one twice: b bThe slice walks your index list, in your order, and does not care that you asked for position 0 three times.
That is what makes it right for column work. A tool writes columns in one order and your report wants them in another:
use strict;
use warnings;
my $line = "alu,smoke,PASS,184,7";
my @field = split /,/, $line, -1;
# tool order : block, test, status, total, fails
# report order: status, block, fails
my @out = @field[2, 0, 4];
print join(" | ", @out), "\n";PASS | alu | 7One expression selected and reordered three of five columns. Writing that with three separate reads would be three statements and three chances to use the wrong index; here the index list sits in one place where it can be checked against the tool's documentation at a glance.
Note the -1 on the split — Chapter 4.7 §2's rule, so a trailing empty column cannot shift the positions.
3. An Out-of-Range Index Gives undef, Silently
use strict;
use warnings;
my @row = ("x", "y");
my @s = @row[0, 5];
printf "count = %d\n", scalar(@s);
printf "contents: [%s]\n", join("][", map { defined $_ ? $_ : "undef" } @s);count = 2
contents: [x][undef]Two indexes in, two elements out — the second one undef. A slice always returns one element per index, padding with undef where the array does not reach.
Nothing on STDERR, which is Chapter 5.1 §5's rule again. The practical consequence for column work is specific and worth stating: a slice of a short row succeeds. If a log line is missing its last two columns, @field[2, 0, 4] still returns three values, two of them real and one undef, and the report prints a row with a gap in it.
So the count check from Chapter 5.1 §5 belongs before the slice, not after it:
use strict;
use warnings;
my @field = split /,/, "alu,smoke", -1;
if (scalar(@field) >= 5) {
print join(" | ", @field[2, 0, 4]), "\n";
}
else {
printf "short row: wanted 5 fields, got %d\n", scalar(@field);
}short row: wanted 5 fields, got 2Chapter 5.8 is devoted to this pattern.
4. A Slice Can Be Assigned To
use strict;
use warnings;
my @a = (1, 2, 3, 4);
@a[1, 2] = ("X", "Y");
print "[@a]\n";[1 X Y 4]The slice on the left names the positions to write; the list on the right supplies the values. Positions 1 and 2 changed and the rest were untouched.
Which makes a swap one line
use strict;
use warnings;
my @pair = ("first", "second");
@pair[0, 1] = @pair[1, 0];
print "[@pair]\n";[second first]No temporary variable. It works because of the rule from Chapter 3.1 §2: the right-hand side is reduced to a finished value — here the list ("second", "first") — before anything is stored. So by the time position 0 is written, the old value has already been read.
That is a genuinely useful idiom and it generalises: @a[0, 1, 2] = @a[2, 0, 1] rotates three elements in one statement.
5. Why This Matters in Engineering Work
Column selection is the main use. A tool report has twelve columns and your summary needs four. @field[0, 3, 7, 11] is one expression whose index list can be read against the tool's column documentation, and it is the whole of the extraction step.
Reordering for presentation. The order a tool writes columns in is rarely the order a reader wants them. A slice handles that without an intermediate structure.
The last few elements. @log[-3 .. -1] is the last three lines, whatever the length. That is a common thing to want when reporting the tail of a run.
And the silence is the hazard, as everywhere in this module. A slice of a short row returns the right number of values and the wrong values, with no diagnostic. The count check goes before the slice. Writing it is one if, and the alternative is a report with gaps that nobody can trace back to a short input line.
6. Common Mistakes
Writing @row[0] for one element. It works and warns. One index wants $.
Expecting a slice to return array order. It returns your index order, and repeats what you repeat.
Expecting an out-of-range index to fail. You get undef in that position, silently, and the right number of elements.
Slicing before checking the field count. A short row slices successfully and gives you undef values.
Assigning fewer values than the slice has positions. The remaining positions become undef, destroying what was there.
Forgetting -1 on the split that produced the array. A dropped trailing column shifts nothing, but it does change the count your guard is testing — Chapter 4.7 §2.
7. Exercise
Exercise 1 — pick three columns. Split "alu,smoke,PASS,184,7" on commas and use one slice to print the status, the block and the fail count, in that order. Confirm you get PASS | alu | 7.
Exercise 2 — the warning. Read one element with @row[1] and record the exact warning and the value. Then say what @row[1] produces and why it collapses to a single value.
Exercise 3 — order and repeats. With a three-element array, print @a[2,1,0] and @a[1,1,1]. Then state in one sentence what the slice is iterating over.
Exercise 4 — the tail. Given an array of six log lines, print the last three with a single slice using negative indexes. Then say why that expression keeps working when the array grows.
Exercise 5 — the short row. Slice positions 0, 2 and 4 out of a two-element array. Report the element count and which values are undef, and confirm STDERR is empty. Then add the guard from §3 and confirm it reports the short row instead.
Exercise 6 — swap and rotate. Swap two elements with a slice assignment. Then rotate three elements so that the last becomes first, in one statement, and explain why no temporary variable is needed.
Exercise 7 — the padding trap. Assign a one-element list to a three-position slice. Print the array with undef made visible and say what happened to the two values you did not supply.
8. Summary
- A slice subscripts with a list of indexes and returns one element per index.
- It takes the
@sigil because it produces several values — the first place@plus a subscript is correct. @row[0]works and warns, suggesting$row[0], because one index produces one value.- The results come back in your index order, and an index may repeat — which makes a slice a column selector.
- Negative indexes work, so
@log[-3 .. -1]is "the last three". - An out-of-range index gives
undefin that position, silently, and the slice still returns one element per index — so a short row slices successfully. - A slice is assignable, which makes
@pair[0,1] = @pair[1,0]a swap with no temporary. - Supplying fewer values than positions sets the rest to
undef, destroying what was there.
Chapter 5.4 moves from reading an array to changing its length — the four functions that add and remove at the two ends, and the one of them that almost every script in this trade uses to collect results.
Where this fits
Part of the Perl curriculum.
