Perl stacks, queues, and word frequencies
Use push, pop, shift, and unshift as stack and queue operations, and count items with a hash.
On this page
Perl arrays and hashes.
- Use push, pop, shift, and unshift as stack and queue operations, and count items with a hash.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
A Perl array is also a stack and a queue. push adds at the end and pop removes from the end, which gives a last-in, first-out stack. shift removes from the front and unshift adds at the front, so push with shift is a first-in, first-out queue. All four operations are efficient, and no extra module is needed.
A hash makes counting easy. $freq{$word}++ creates the key with the value 1 the first time, because an undefined value counts as zero in numeric context. grep filters a list with a condition, map transforms each element, and the idiom grep { !$seen{$_}++ } removes duplicates while keeping the first occurrence of each item.
Code example
use strict;
use warnings;
my @stack;
push @stack, 1, 2, 3;
my $top = pop @stack;
print "$top @stack\n";
my @queue = (1, 2, 3);
my $front = shift @queue;
push @queue, 4;
print "$front @queue\n";
unshift @queue, 0;
print "@queue\n";
my $text = "the cat and the hat and the bat";
my %freq;
$freq{$_}++ for split ' ', $text;
print join(" ", map { "$_=$freq{$_}" } sort { $freq{$b} <=> $freq{$a} or $a cmp $b } keys %freq), "\n";
my @even = grep { $_ % 2 == 0 } 1 .. 10;
my @squares = map { $_ ** 2 } @even;
print "@even | @squares\n";
my %seen;
my @unique = grep { !$seen{$_}++ } qw(a b a c b);
print "@unique\n";3 1 2 1 2 3 4 0 2 3 4 the=3 and=2 bat=1 cat=1 hat=1 2 4 6 8 10 | 4 16 36 64 100 a b c
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
use strict;use warnings; my @stack;push @stack, 1, 2, 3;my $top = pop @stack;print "$top @stack\n"; my @queue = (1, 2, 3);my $front = shift @queue;push @queue, 4;print "$front @queue\n";unshift @queue, 0;print "@queue\n"; my $text = "the cat and the hat and the bat";my %freq;$freq{$_}++ for split ' ', $text;print join(" ", map { "$_=$freq{$_}" } sort { $freq{$b} <=> $freq{$a} or $a cmp $b } keys %freq), "\n"; my @even = grep { $_ % 2 == 0 } 1 .. 10;my @squares = map { $_ ** 2 } @even;print "@even | @squares\n"; my %seen;my @unique = grep { !$seen{$_}++ } qw(a b a c b);print "@unique\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
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| pop removes the last element: the most recently pushed one. | — | — | |
| 3 came out; 1 and 2 remain. | — | 3 1 2 | |
| shift removes the first element: the oldest one. | — | 3 1 2 | |
| 1 left the front; 4 joined the back. | — | 3 1 2 1 2 3 4 | |
| After unshift, 0 is at the front. | — | 3 1 2 1 2 3 4 0 2 3 4 | |
| Count every word. A missing key starts at zero. | — | 3 1 2 1 2 3 4 0 2 3 4 | |
| Highest counts first, ties broken alphabetically. | — | 3 1 2 1 2 3 4 0 2 3 4 the=3 and=2 bat=1 cat=1 hat=1 | |
| grep keeps the even numbers and map squares them. | — | 3 1 2 1 2 3 4 0 2 3 4 the=3 and=2 bat=1 cat=1 hat=1 2 4 6 8 10 | 4 16 36 64 100 | |
| !$seen{$_}++ is true only the first time an item is seen. | — | 3 1 2 1 2 3 4 0 2 3 4 the=3 and=2 bat=1 cat=1 hat=1 2 4 6 8 10 | 4 16 36 64 100 a b c |
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
push, pop, shift, and unshift are O(1) amortized. Counting is O(n).
- shift and pop on an empty array return undef, and with use warnings, printing undef gives a warning.
- In grep and map blocks, $_ is the current element and is an alias to the original. Modifying it changes the array.
- Hash order is random. Sort the keys whenever the output order matters.
Try it yourself
How would you count only words longer than three letters?
Reveal answer and reasoning
Add a condition: $freq{$_}++ for grep { length($_) > 3 } split ' ', $text;