Skip to content
VLSI Mentor

Perl Part I · Module 6 · Hashes

keys, values and each

Having read one key at a time, the next thing you want is all of them. Two functions give you that: one returns every key as a list and one returns every value. A third walks the pairs one at a time, and it is the one worth being careful with — because it does not return a list, it returns the next pair, which means it has to remember where it got to. That memory belongs to the hash rather than to your loop, so it is shared by every piece of code that walks the same hash. Break out of such a loop early and the marker stays where you left it, so the next walk over that hash starts partway through and quietly visits fewer keys than exist.

Foundation14 min readPerlPart IFundamentalsHashesDebugging

Part I · Module 6 · Chapter 6.5 · keys, values and each

1. keys and values

Azvya Education Pvt. Ltd.VLSI Mentor
keys-and-values.pl
use strict;
use warnings;

my %marks = (maths => 72, physics => 65, chemistry => 90);

printf "keys   : %s\n", join(" ", sort keys %marks);
printf "values : %s\n", join(" ", sort { $a <=> $b } values %marks);
printf "count  : %d\n", scalar(keys %marks);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
keys   : chemistry maths physics
values : 65 72 90
count  : 3
  • keys %h returns every key as a list.
  • values %h returns every value as a list.
  • scalar(keys %h) is the key count, as Chapter 6.1 §4 established.

Both return a list, so everything from Module 5 applies: you can sort them, grep them, map over them, count them, or assign them to an array.

Note that both outputs above are sorted. That is not decoration — keys returns the keys in an order you do not control and which is not stable between runs. Chapter 6.6 is entirely about that, and until then every example in this chapter sorts what it prints.

The standard loop

Azvya Education Pvt. Ltd.VLSI Mentor
walk-sorted.pl
use strict;
use warnings;

my %marks = (maths => 72, physics => 65, chemistry => 90);

for my $subject (sort keys %marks) {
    printf "%-10s %3d\n", $subject, $marks{$subject};
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
chemistry   90
maths       72
physics     65

for my $k (sort keys %h) is the loop to learn. It gives you the key, from which you get the value with $h{$k}, in an order that is the same every run.

This is the form you will write ninety per cent of the time. The rest of the chapter is the other two functions and why they are the other ten.

2. values Are Aliases

Azvya Education Pvt. Ltd.VLSI Mentor
values-alias.pl
use strict;
use warnings;

my %h = (a => 1, b => 2);

for my $v (values %h) {
    $v *= 10;
}

printf "after the loop: %s\n", join(" ", map { "$_=$h{$_}" } sort keys %h);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
after the loop: a=10 b=20
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet

The hash changed. Multiplying the loop variable multiplied the values in %h, with no warning.

values does not return copies — it returns aliases to the values stored in the hash. This is the same alias behaviour Chapter 5.10 §4 measured for $_ inside map, and the same thing a foreach loop variable does over an array.

3. each Returns One Pair at a Time

Azvya Education Pvt. Ltd.VLSI Mentor
each-loop.pl
use strict;
use warnings;

my %marks = (maths => 72, physics => 65, chemistry => 90);

while (my ($subject, $mark) = each %marks) {
    printf "%-10s %3d\n", $subject, $mark;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — ONE RUN; a second run gives a different order
chemistry   90
physics     65
maths       72

Running that script twice produced two different orderings on the same machine with the same input — which is not a quirk of each but a property of the hash, and is Chapter 6.6's subject.

each %h returns the next key/value pair as a two-element list, and an empty list when it runs out — which is what ends the while loop, by Chapter 5.7's emptiness rule.

Its appeal is that it does not build a list of all the keys first, which matters for a hash with millions of entries. For the hashes in a typical flow script, that saving is irrelevant.

Its cost is the hidden state, which is the rest of this chapter.

4. The Hidden Iterator Is Shared With the Hash

each has to remember where it got to between calls. That marker lives in the hash, not in your loop — so every piece of code walking that hash shares one marker.

Breaking out of a loop leaves it mid-way

Azvya Education Pvt. Ltd.VLSI Mentor
iterator-left-behind.pl
use strict;
use warnings;

my %h = (a => 1, b => 2, c => 3);

my $first = 0;
while (my ($k) = each %h) {
    $first++;
    last if $first == 1;
}

my $second = 0;
while (my ($k) = each %h) {
    $second++;
}

printf "first loop saw  : %d\n", $first;
printf "second loop saw : %d  (the hash has %d keys)\n", $second, scalar(keys %h);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
first loop saw  : 1
second loop saw : 2  (the hash has 3 keys)

The second loop saw two keys, not three.

The first loop consumed one pair and then last jumped out, leaving the marker pointing at the second entry. The second loop resumed from there, visited the remaining two, and stopped — believing it had seen the whole hash.

Nothing warns. The second loop processed two thirds of the data and reported success.

keys resets the marker

Azvya Education Pvt. Ltd.VLSI Mentor
keys-resets.pl
use strict;
use warnings;

my %h = (a => 1, b => 2, c => 3);

my ($k1) = each %h;

keys %h;                       # called purely for the reset

my ($k2) = each %h;

printf "first each  : %s\n", $k1;
printf "second each : %s\n", $k2;
printf "same key    : %s\n", ($k1 eq $k2 ? "yes — the iterator was reset" : "no");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — one run; which key appears varies, see below
first each  : b
second each : b
same key    : yes — the iterator was reset

Calling keys %h — even discarding the result — resets the marker to the beginning. So the second each returned the same key as the first.

Note which key that was: b, not a. A second run of the identical script prints a different letter, because the key each reaches first is whichever one the hash happens to put first — and that is not stable between runs. The comparison is the stable part of this output; the key itself is not. Chapter 6.6 is the chapter on exactly that, and this example is a small demonstration of why it needs one.

That is the documented way to reset it, and the fact that a function called for its side effect is the official fix is itself an argument for not getting into the situation.

Two loops over the same hash at once do not work

Azvya Education Pvt. Ltd.VLSI Mentor
interleaved.pl
use strict;
use warnings;

my %h = (a => 1, b => 2, c => 3);

my $outer = 0;

while (my ($k) = each %h) {
    $outer++;
    last if $outer > 10;                 # a guard, not part of the logic

    my $inner = 0;
    while (my ($k2) = each %h) {
        $inner++;
        last if $inner > 10;
    }
}

printf "outer iterations: %d  (the hash has %d keys)\n", $outer, scalar(keys %h);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
outer iterations: 11  (the hash has 3 keys)

Eleven, on a three-key hash — and it only stopped because of the guard.

The inner loop shares the outer loop's marker, so each time it runs it consumes the remaining entries and then the exhausted marker resets, letting the outer loop start again. Without last if $outer > 10 this does not terminate.

A nested walk over one hash is a real thing to want — comparing every entry against every other — and each cannot do it. keys can, because each loop gets its own list.

5. Choosing Between Them

You wantUse
every key, in a stable orderfor my $k (sort keys %h)
the key countscalar(keys %h)
every value, read onlyfor my $v (values %h)
to transform every value in placefor my $v (values %h) { ... } — deliberately
a filtered or sorted subsetgrep/sort over keys %h
one pair at a time from a very large hasheach, with no early exit
Azvya Education Pvt. Ltd.VLSI Mentor
common-patterns.pl
use strict;
use warnings;

my %errors = (alu => 0, fifo => 7, decoder => 2, regfile => 0);

printf "blocks tracked : %d\n", scalar(keys %errors);
printf "total errors   : %d\n", eval { my $t = 0; $t += $_ for values %errors; $t };
printf "clean blocks   : %s\n", join(" ", sort grep { $errors{$_} == 0 } keys %errors);
printf "worst first    : %s\n", join(" ", sort { $errors{$b} <=> $errors{$a} or $a cmp $b } keys %errors);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
blocks tracked : 4
total errors   : 9
clean blocks   : alu regfile
worst first    : fifo decoder alu regfile

Four different questions, all answered through keys or values and Module 5's list functions:

  • The count is scalar(keys %h).
  • The total sums values %h.
  • The filter is grep over keys, comparing with == because the values are counts — Chapter 3.5.
  • The ordering is a comparator that looks the value up by key, with a tie-break so the result is reproducible — Chapter 5.9 §5.

The eval { ... } on the total line is only there to fit a multi-statement sum into a printf argument; in real code that is a two-line loop.

6. Why This Matters in Engineering Work

for my $k (sort keys %h) is the reporting loop. Every per-block, per-test, per-corner summary is this shape, and the sort is what makes today's report comparable with yesterday's — Chapter 6.6 is the chapter on why.

Summing or averaging values is values. Total errors, total cycles, mean utilisation. No key needed, so no loop over keys.

The alias behaviour is a tool and a trap. Trimming or scaling every value in place through values is the idiomatic form and worth knowing. Doing it accidentally — in a loop you thought was read-only — silently rewrites your data, and use warnings says nothing.

And each is the one function in this module to be wary of. The failure mode is a loop that processes part of a hash and reports success, caused by an early exit in an unrelated loop somewhere else. On a hash small enough to fit in a report — which is all of them in practice — keys costs nothing and has no shared state.

7. Common Mistakes

Printing keys %h unsorted. The order is not stable between runs — Chapter 6.6.

Modifying the loop variable in a values loop by accident. It rewrites the hash.

Expecting keys to alias like values. Assigning to a key loop variable changes nothing in the hash.

Using last inside an each loop. The marker stays put and truncates the next walk over that hash.

Nesting two each loops over one hash. They share the marker and the outer loop does not terminate.

Calling each on a hash someone else may also be walking. The state is in the hash, not in your code.

Reaching for each for efficiency on a small hash. The saving is nil and the risk is not.

8. Exercise

Exercise 1 — the three functions. For a four-key hash, print the sorted keys, the sorted values and the key count.

Exercise 2 — the standard loop. Write for my $k (sort keys %h) over a hash of blocks and error counts, printing an aligned two-column table.

Exercise 3 — the alias. Multiply every value by 10 through a values loop, then print the hash. Confirm it changed and that STDERR is empty. Then do it again via a copied array and confirm the hash is untouched.

Exercise 4 — keys do not alias. In a for my $k (keys %h) loop, assign a new string to $k. Print the hash's keys afterwards and say what changed.

Exercise 5 — the left-behind iterator. Reproduce §4's first script. Report how many keys each loop saw and the hash's real key count. Then insert keys %h; between the two loops and report the second count again.

Exercise 6 — the interleaved loops. Run §4's nested script. Report the outer iteration count against the key count, then rewrite it with keys so a nested walk works correctly.

Exercise 7 — four questions. For a hash of five blocks and error counts, produce the count, the total, the list of clean blocks and the worst-first ordering — each in one statement, each using keys or values.

9. Summary

  • keys %h and values %h each return a list, so Module 5's list functions all apply.
  • scalar(keys %h) is the key count.
  • for my $k (sort keys %h) is the loop to write — stable order, no shared state.
  • values returns aliases, so assigning to the loop variable rewrites the hash. That is the idiomatic in-place transform and an easy accident.
  • keys does not alias; a key is not a writable location.
  • each returns one pair at a time and keeps its marker inside the hash.
  • Breaking out of an each loop truncates the next one — two of three keys, silently.
  • Two each loops over one hash share the marker and the outer one does not terminate.
  • keys %h resets the marker, and needing a side-effect call is itself a reason to prefer keys.

Chapter 6.6 explains why every example in this chapter sorted its output: hash order is not merely unspecified, it genuinely differs from one run of the same script to the next.

Where this fits

Part of the Perl curriculum.