Skip to content
VLSI Mentor

Perl Part I · Module 6 · Hashes

Hashes: Key/Value Lookup

Module 5 held values in order and found them by counting. A hash holds them by name: a mark for each subject, a status for each test, a count for each kind of warning. You hand it a key and it hands back the value, with no searching and no position to keep track of — which is the structure Chapter 5.11 kept pointing forward to, both for asking whether something has been seen before and for removing duplicates. The syntax follows the sigil rule you already know, with one new wrinkle that is worth getting right immediately: an element is subscripted with braces rather than brackets, and that brace is the only thing distinguishing a hash element from an array element of the same name. They are different variables and they can both exist at once.

Foundation13 min readPerlPart IFundamentalsHashesData Structures

Part I · Module 6 · Chapter 6.1 · Hashes: Key/Value Lookup

1. Lookup by Name

Azvya Education Pvt. Ltd.VLSI Mentor
first-hash.pl
use strict;
use warnings;

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

print "maths     : $marks{maths}\n";
print "physics   : $marks{physics}\n";
print "key count : ", scalar(keys %marks), "\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
maths     : 72
physics   : 65
key count : 3

A hash is declared with my %name and built from a list of alternating keys and values — "maths" then 72, "physics" then 65, and so on.

To read one value you give its key: $marks{maths}. There is no position involved. The key is the name, and the name is how you get back.

That list form with plain commas is readable enough here, and Chapter 6.2 shows the notation Perl programmers actually use for it — which makes the pairing visible rather than something you count.

keys %marks gives every key as a list, which is Chapter 6.5's subject; here it is a measuring instrument, wrapped in scalar() from Chapter 2.10 to get the count.

2. Three Sigils, and the Brace

The sigil rule from Chapter 5.1 §2 still holds — the sigil describes what the expression produces — and now there is a third container to apply it to.

You writeYou getSigil
%marksthe whole hash% — pairs
$marks{maths}one value$ — one value
@marks{...}several values@ — a hash slice, Chapter 6.7

What is new is the subscript: an array element uses [ ] and a hash element uses { }.

Azvya Education Pvt. Ltd.VLSI Mentor
brackets-vs-braces.pl
use strict;
use warnings;

my @by_position = ("zero", "one", "two");
my %by_name     = (0, "ZERO", 1, "ONE");

print "array element \$by_position[1] : $by_position[1]\n";
print "hash element  \$by_name{1}     : $by_name{1}\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
array element $by_position[1] : one
hash element  $by_name{1}     : ONE

Both subscripts are 1 and both expressions start with $. The brace versus bracket is the only difference, and it selects a completely different variable.

3. A Hash Does Not Interpolate

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

my %marks = ("maths", 72);

print "the whole hash : %marks\n";
print "one element    : $marks{maths}\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
the whole hash : %marks
one element    : 72

"%marks" printed the two literal characters %marks. There is no interpolation of a whole hash, which is a real difference from arrays — Chapter 5.7 §2 measured that "@marks" does interpolate its elements.

The reason is practical: % is a common literal character in text — percentages, printf formats — so making it a sigil inside strings would break far more than it helped. Chapter 4.2 §5 showed @ needing an escape in an email address for the opposite reason.

An element does interpolate, as the second line shows. To print a whole hash you have to build the text yourself, which Chapter 6.6 covers properly because the order is its own question.

4. Counting Keys, and One Version-Sensitive Trap

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

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

printf "scalar(keys \%%marks) : %d\n", scalar(keys %marks);
printf "scalar(keys \%%empty) : %d\n", scalar(keys %empty);

printf "is \%%marks non-empty : %s\n", (%marks ? "yes" : "no");
printf "is \%%empty non-empty : %s\n", (%empty ? "yes" : "no");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
scalar(keys %marks) : 3
scalar(keys %empty) : 0
is %marks non-empty : yes
is %empty non-empty : no

scalar(keys %hash) is the count of keys. That is the form to write.

And a hash in a boolean test works the way Chapter 5.7 §3's array idiom did: if (%hash) asks "does this hash have anything in it". Empty is false, anything else is true.

5. Reading a Key That Was Never Set

Azvya Education Pvt. Ltd.VLSI Mentor
missing-key.pl
use strict;
use warnings;

my %result = ("alu", "PASS");

my $good = $result{alu};
my $typo = $result{aul};

printf "correct key : [%s]\n", $good;
printf "typo'd key  : [%s]\n", (defined $typo ? $typo : "undef");
printf "key count   : %d\n", scalar(keys %result);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — STDOUT
correct key : [PASS]
typo'd key  : [undef]
key count   : 1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet

$result{aul} — the key transposed — gave undef, with nothing on STDERR, and the hash still has one key.

This is the same hazard as Chapter 5.1 §5's out-of-range array read, and it is worth seeing as one lesson rather than two: Perl's containers do not complain about a member that is not there. Arrays are silent about a position past the end; hashes are silent about a key that was never set.

What makes the hash version worse in practice is that a key is a name, and names can be misspelled in ways a position cannot:

  • A typo — aul for alu — looks right at a glance.
  • A case difference — $result{ALU} against a hash keyed on alu — is a different key entirely.
  • A key with trailing whitespace from an unchomped line — $result{"alu\n"} — is a different key, which is Chapter 4.9 arriving in a new place.

All three give undef and no diagnostic. The value then interpolates as nothing by Chapter 4.2 §6, or prints as 0 under %d by Chapter 4.8 §6.

Chapter 6.3 covers the consequences, and Chapter 6.4 gives the test that distinguishes "never set" from "set to nothing".

6. What a Hash Is For

Azvya Education Pvt. Ltd.VLSI Mentor
per-test-status.pl
use strict;
use warnings;

my %status = (
    "alu_smoke_01", "PASS",
    "fifo_wr_02",   "FAIL",
    "decoder_03",   "PASS",
);

for my $test ("fifo_wr_02", "decoder_03") {
    printf "%-14s %s\n", $test, $status{$test};
}

printf "tracked %d test(s)\n", scalar(keys %status);
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
fifo_wr_02     FAIL
decoder_03     PASS
tracked 3 test(s)

Each lookup is direct. No loop over the tests, no comparison, no position.

Compare the array version of the same job, which Chapter 5.11 §1 had to use:

Azvya Education Pvt. Ltd.VLSI Mentor
The array version, for contrast
my @rows = ("alu_smoke_01 PASS", "fifo_wr_02 FAIL", "decoder_03 PASS");

for my $wanted ("fifo_wr_02") {
    for my $row (@rows) {
        my ($name, $st) = split " ", $row;
        print "$name $st\n" if $name eq $wanted;
    }
}

Two nested loops, a split per row per lookup, and the whole list scanned every time. That is what Chapter 5.11 §1's callout meant by "the wrong shape": grep works and does not scale, and the hash is the structure that does.

Chapter 6.11 is the chapter about choosing between them properly. The short version: order matters, use an array; lookup by name matters, use a hash.

7. Why This Matters in Engineering Work

A result per test, a count per category, a setting per name. These are the three shapes that come up most, and all three are hashes. A regression summary keyed by test name, a tally of warning types, a configuration read from a file.

Lookup cost stops mattering. A hash finds a key without scanning, so a check inside a loop over thousands of items stays cheap. That is the specific reason Chapter 5.11 §1 deferred membership testing to this module.

The silent miss is the thing to design against. A misspelled key is a plausible-looking typo that produces undef with no diagnostic, and undef then renders as nothing or as zero. In a report where zero means "no errors", that is the worst available substitution — and Chapter 6.4 exists because telling "absent" from "present but empty" needs a deliberate test.

And naming matters as much as it did for arrays. %status says both that it is keyed lookup and what it holds. %data says neither, and because %data and @data can coexist, a vague name plus a punctuation slip is a silent read of the wrong variable.

8. Common Mistakes

Using [ ] for a hash element. $marks[maths] is an array subscript on a different variable.

Expecting "%hash" to interpolate. It prints the literal characters. Only an element interpolates.

Using my $n = %hash as a key count. Correct on 5.26+ and a bucket string before that. Write scalar(keys %hash).

Expecting a missing key to warn. It gives undef, silently.

Forgetting that keys are case-sensitive and whitespace-sensitive. ALU, alu and "alu\n" are three different keys.

Naming an array and a hash the same thing. Legal, and it turns a brace-for-bracket typo into a silent wrong read.

Building a hash from a list and counting the commas by eye. Chapter 6.2 has the notation that makes the pairing visible.

9. Exercise

Exercise 1 — build and read. Create a hash of four subjects and marks using the plain-comma list form. Print two of the marks by key and the number of keys. Say which sigil each of those lines needed and why.

Exercise 2 — bracket against brace. Declare @thing and %thing in one script, give each a member, and print both with $thing[0] and $thing{0}. Confirm you get two different values, then say in one clause why you would not write this in real code.

Exercise 3 — interpolation. Print a hash with "%h" and then an element with "$h{key}". Record both outputs and state the rule.

Exercise 4 — two ways to count. For a three-key hash, print scalar(keys %h) and my $n = %h. Record both values, then say which you would commit and what would happen to the other on Perl 5.22.

Exercise 5 — the silent miss. Read a key that exists and a key with two letters transposed. Print both results and whether each is defined, and capture STDERR separately to confirm it is empty.

Exercise 6 — three keys that look the same. Store values under alu, ALU and "alu\n" in one hash. Print the key count and then each value. Explain why the count is what it is, naming the chapter that covers the third case.

Exercise 7 — replace the nested loop. Take §6's array version and rewrite it with a hash so each lookup is one statement. Then state what the array version did once per lookup that the hash version does not do at all.

10. Summary

  • A hash looks a value up by key, not by position, and is declared with my %name from a list of alternating keys and values.
  • %marks is the hash; $marks{maths} is one value — the sigil describes what the expression produces.
  • Hash elements subscript with { }; array elements with [ ]. That brace is the only difference between $a{0} and $a[0], which are different variables.
  • A whole hash does not interpolate into a string; an element does.
  • scalar(keys %h) is the portable key count. my $n = %h gives the count only on Perl 5.26 and later.
  • if (%h) tests emptiness and is safe on every version.
  • Reading a key that was never set gives undef, silently — the same hazard as an out-of-range array read, made worse because keys can be misspelled.
  • Keys are case- and whitespace-sensitive, so alu, ALU and "alu\n" are three keys.

Chapter 6.2 covers the notation that makes a hash literal readable, and the diagnostic you get when the list of pairs turns out to have an odd number of items in it.

Where this fits

Part of the Perl curriculum.