Perl Part I · Module 5 · Arrays and Lists
push, pop, shift and unshift
Everything so far has read an array that already existed. These four functions change its length: two work at the end and two at the front, one of each pair adding and the other removing. The one you will use most is push, because it is how a script collects results while it works — walk a set of tests, push the name of each one that failed, and at the end you have both the list and the count. The detail worth knowing before you rely on them is that the two halves return different kinds of thing. The adding functions hand back the array's new length; the removing functions hand back the element they took out. Assigning the result of the wrong one gives you a number where you wanted a value, and nothing warns.
Foundation14 min readPerlPart IFundamentalsArraysIdioms
Part I · Module 5 · Chapter 5.4 · push, pop, shift and unshift
1. Four Functions, Two Ends
| Function | End | Does | Returns |
|---|---|---|---|
push | back | adds | the new length |
pop | back | removes | the element removed |
unshift | front | adds | the new length |
shift | front | removes | the element removed |
use strict;
use warnings;
my @q = ("b", "c");
my $after_push = push @q, "d";
my $after_unshift = unshift @q, "a";
printf "after push returned %d -> [%s]\n", $after_push, join(" ", @q);
printf "after unshift returned %d -> [%s]\n", $after_unshift, join(" ", @q);
my $popped = pop @q;
my $shifted = shift @q;
printf "pop returned [%s] -> [%s]\n", $popped, join(" ", @q);
printf "shift returned [%s] -> [%s]\n", $shifted, join(" ", @q);after push returned 3 -> [a b c d]
after unshift returned 4 -> [a b c d]
pop returned [d] -> [b c]
shift returned [a] -> [b c]Read the two printf lines for push and unshift carefully. The array printed on both is [a b c d], because both printfs ran after both modifications — but the returned values were 3 and 4, the lengths immediately after each call.
All four modify the array in place. That is the exception to Chapter 3.1 §5's rule, and the same exception the four-argument substr made in Chapter 4.5 §4.
2. The Return Values Are Not Symmetric
use strict;
use warnings;
my @a = ("x", "y");
my $from_push = push @a, "z";
my $from_pop = pop @a;
printf "push gave [%s] <- a LENGTH\n", $from_push;
printf "pop gave [%s] <- an ELEMENT\n", $from_pop;push gave [3] <- a LENGTH
pop gave [z] <- an ELEMENTpush returned 3. Not "z", not the array — the count of elements afterwards.
3. pop and shift on an Empty Array Are Silent
use strict;
use warnings;
my @empty = ();
my $popped = pop @empty;
my $shifted = shift @empty;
printf "pop gave [%s]\n", (defined $popped ? $popped : "undef");
printf "shift gave [%s]\n", (defined $shifted ? $shifted : "undef");
printf "count still %d\n", scalar(@empty);pop gave [undef]
shift gave [undef]
count still 0Both returned undef, the array stayed empty, and nothing was reported. Taking from an empty array is not an error in Perl.
That silence is what makes the common queue idiom work:
use strict;
use warnings;
my @work = ("alu", "fifo", "decoder");
while (my $job = shift @work) {
print "processing $job\n";
}
print "queue empty\n";processing alu
processing fifo
processing decoder
queue emptyThe loop ends because shift eventually returns undef, which is false.
4. push Takes Several Values, and Flattens an Array
use strict;
use warnings;
my @a = ("x");
push @a, "y", "z";
printf "after two values : count=%d [%s]\n", scalar(@a), join(" ", @a);
my @more = ("p", "q");
push @a, @more;
printf "after push \@more : count=%d [%s]\n", scalar(@a), join(" ", @a);after two values : count=3 [x y z]
after push @more : count=5 [x y z p q]Pushing an array adds its elements, not the array as a single item. The result is five elements, flat.
That is not a special behaviour of push — it is how lists work in Perl generally, and Chapter 5.12 is the chapter devoted to it. It is convenient here and it is also why you cannot build a list of lists this way; Module 7's references are the answer to that.
And push needs no initialiser:
use strict;
use warnings;
my @failures;
push @failures, "fifo";
printf "count=%d [%s]\n", scalar(@failures), join(" ", @failures);count=1 [fifo]my @failures; is an empty array, as Chapter 5.1 §4 established, and pushing onto it works with no warning.
5. The Pattern Worth Learning: Collect As You Go
This is what the canonical index means by "how a script collects results as it goes", and it is the shape of most reporting scripts in this trade.
use strict;
use warnings;
my @rows = ("alu PASS", "fifo FAIL", "decoder PASS", "regfile FAIL");
my @failures;
for my $row (@rows) {
my ($name, $status) = split " ", $row;
push @failures, $name if $status eq "FAIL";
}
printf "%d failure(s): %s\n", scalar(@failures), join(", ", @failures);2 failure(s): fifo, regfileFour lines of logic, and look at what the array gives you at the end:
- The count —
scalar(@failures)is the number for the report. - The names —
join(", ", @failures)is the list for the reader. - The emptiness — on a clean run
@failuresis empty, which Chapter 5.7 turns into a one-word test.
Three pieces of a report from one accumulator. That is why push earns its Critical rating in the index.
Three borrowed constructs are credited rather than taught here: the for loop is Module 9's, my ($name, $status) = split ... is list assignment which Chapter 5.6 covers, and split " " is Chapter 4.7 §5's whitespace form — chosen deliberately over split / / for the reason that chapter measured.
Collecting rows rather than names
The same pattern with more per entry, formatted with Chapter 4.8's sprintf:
use strict;
use warnings;
my @rows = ("alu 184 177", "fifo 92 92", "decoder 1240 1198");
my @report;
for my $row (@rows) {
my ($name, $total, $passed) = split " ", $row;
push @report, sprintf("%-10s %6d %6d %6.1f%%",
$name, $total, $passed, $passed / $total * 100);
}
print "$_\n" for @report;alu 184 177 96.2%
fifo 92 92 100.0%
decoder 1240 1198 96.6%Building the lines into an array rather than printing them immediately means you can sort them, count them, or decide not to emit them at all — the same argument Chapter 3.4 §6 made for accumulating a string with .=, now with the pieces kept separate so they remain sortable.
6. Which End, and Why It Matters
A stack uses one end: push and pop. The last thing in is the first thing out.
A queue uses both: push to add at the back, shift to take from the front. The first thing in is the first thing out.
use strict;
use warnings;
my @stack = ();
push @stack, $_ for ("a", "b", "c");
printf "stack pops : %s %s %s\n", pop @stack, pop @stack, pop @stack;
my @queue = ();
push @queue, $_ for ("a", "b", "c");
printf "queue shifts: %s %s %s\n", shift @queue, shift @queue, shift @queue;stack pops : c b a
queue shifts: a b cFor scripting work the distinction is usually about whether order matters to the reader. A list of failures should come out in the order they happened, which means push to collect and then read the array normally — not pop, which would reverse it.
unshift is the rarest of the four. Its common use is putting something at the front of a list of arguments or a list of paths to search, where "try this first" is the meaning.
7. Why This Matters in Engineering Work
Collecting failures, warnings or violations. §5's pattern, and it is everywhere. The array gives you the count and the detail from one pass.
Building report lines before deciding what to do with them. Accumulate, then sort with Chapter 5.9, then print. A script that prints as it goes cannot sort its own output.
Work queues. A list of blocks to process, with shift taking the next one. Use while (@work) rather than while (my $job = shift @work) so that a job named 0 does not end the run — §3's callout is a real bug, not a theoretical one.
And the return-value asymmetry costs real time because the failure is a number where a name should be. $added holding 3 instead of "fifo" means a later comparison against a block name never matches, which reads as a data problem. Calling these functions as statements avoids the whole class.
8. Common Mistakes
Assigning push's return value. It is the new length, not the value pushed. Call it as a statement.
Expecting pop to return the array. It returns the one element it removed.
Using while (my $x = shift @a) on data that can be false. A 0 or an empty string ends the loop early. Use while (@a) or defined(...).
Expecting pop on an empty array to complain. It returns undef, silently.
Expecting push @a, @b to nest. It flattens — Chapter 5.12.
Using pop to read a collected list. It consumes the array and reverses the order. Read it with a loop or join.
Initialising with = () because push needs it. It does not; my @a; is enough.
9. Exercise
Exercise 1 — all four, with return values. Start from a two-element array. Apply each of the four functions in turn, printing what each returned and the array afterwards. Then fill in the four-row table from §1 from your own output.
Exercise 2 — the count trap. Write my $got = push @names, "alu"; and print $got. Record the value, say what you expected, and name the chapter where you met the same shape of bug with a different function.
Exercise 3 — the empty array. Pop and shift from an empty array, printing whether each result is defined and the count afterwards. Confirm STDERR is empty.
Exercise 4 — break the queue idiom. Build a work list containing "alu", 0 and "decoder", then drain it with while (my $job = shift @work). Record which jobs were processed. Then fix it two ways — with defined and with while (@work) — and confirm all three jobs run.
Exercise 5 — collect the failures. Given six rows of name STATUS, build an array of the failing names with push, then print the count and the comma-joined list. Confirm an all-passing input gives a count of 0 and an empty list.
Exercise 6 — flatten. Push a two-element array onto a one-element array and report the final count. Say what the count would be if push nested instead, and name the chapter that covers why it does not.
Exercise 7 — stack against queue. Collect three items, then empty the array with pop three times, and again with shift three times. Report both orders and say which one you would use to report failures in the order they occurred.
10. Summary
push/popwork at the back;unshift/shiftat the front. All four modify the array in place.pushandunshiftreturn the new length;popandshiftreturn the element removed. Assigning the adding functions' result gives you a count — the same trap aschomp.- Call
pushandunshiftas statements. popandshifton an empty array returnundefsilently.while (my $x = shift @a)stops on a false element, not on an empty array. Preferwhile (@a).pushaccepts several values and flattens an array into the target.my @a;needs no= ()before pushing.- The collect-as-you-go pattern gives you count, detail and emptiness from one pass, which is why it is the module's most-used idiom.
Chapter 5.5 covers the one function that works in the middle of an array rather than at its ends — removing, inserting and replacing a span, with a return value that changes depending on the context it is called in.
Where this fits
Part of the Perl curriculum.
