Concepts / Default values and sentinel values

Default values and sentinel values

The source gives the integer case: the default for an integer type is 0.

  • Programming

Asking a Map a Question

A map pairs keys with values. You give it a key of one type, and it hands back the value of a second type that is associated with that key. The source material makes this concrete with a phone book: a name goes in, a telephone number comes out. This article looks at the single moment you ask a map for a value by writing a key in square brackets, an operation called subscripting. The goal is to understand exactly what happens inside that one line of code, including the case where the key you asked about has never been stored. That case is where many learners' expectations go wrong, so it gets the most attention.

What do you think happens?

A function takes a name and returns whatever value the phone book gives back for that name. If the name has never been stored as a key, what do you think the lookup returns?

  • Nothing at all, or an error, because the name is missing

  • A default value of 0, and the map gains a new entry for that name

  • The most recently stored telephone number in the map

Reveal answer
Answer:

A default value of 0 is returned for the integer value type, and the map gains a new entry for that name.

The lookup has a second behavior that is easy to overlook. If the key is not found, the map creates an entry for that key and gives it a default value. The lookup both returns a value and changes the map.

A Phone Book as a Map

Definition

When a map is indexed by a value of its first type, the map is searched for that key. If the key is present, the map returns the value stored with it. The first type is called the key, and the second type is called the value, or the mapped type. In the phone book example, the key type is a string (a name) and the value type is an integer (a telephone number).

Picture the map as a set of pairs. Each key sits next to exactly one value. The diagram below shows two illustrative entries. A lookup starts from the key you supply, finds the pair that holds it, and reads off the value on the other side. The sample names and numbers are invented for illustration; the source describes the structure, not these particular entries.

Anakey4417valueCarakey9032valuemaps tomaps to
Which stored value does the key Cara point to, and how does the key locate it?

Subscripting Finds a Stored Value

The function below is the entire lookup described in the source. It takes a string, uses that string as a key into a map called phone_book, and returns whatever value the map gives back. Notice that it does not loop, search, or compare anything by hand. The subscript operation performs the entire search, and the function is a named wrapper around that one operation. This is why the source describes subscripting as essentially the lookup itself.

Notes on the code
  • phone_book[name] is the subscript: name is the key being searched for
  • return passes back whatever value the map gives for that key
Worked example

Looking up a key that is present

The phone book already contains the key Cara with the value 9032. What does get_number return when called with Cara?

  1. 1
    Identify the key

    The argument is the string Cara, which is used as the key for the subscript.

  2. 2
    Search the map

    The map is searched for the key Cara. The key is present, so the search succeeds.

  3. 3
    Read the stored value

    The value stored with Cara is 9032. The map returns that value.

  4. 4
    Check for side effects

    Because the key was already present, nothing is added. The map is unchanged.

Result

get_number returns 9032, and the phone book still has exactly the same entries as before.

What Happens When the Key Is Absent

The subscript has two branches, and the choice between them depends entirely on whether the key is already in the map at the moment the subscript runs. The flowchart below traces both paths from the same starting point. In the first branch the map already holds the key, so nothing is added and the stored value comes back. In the second branch the key is absent, so the map grows by one entry before the default value is returned.

Subscript with keyKey in map?Return stored valueValue returnedInsert key withdefault 0Return default 0yesno
Following a subscript with a key, which branch is taken when the key exists and when it does not, and what does each branch return?

The branch that matters most for surprises is the second one. A learner who expects a missing key to produce only a notification of absence is following a model the source does not describe. Instead, the map creates an entry for the missing key and gives it a default value, and that default is what the lookup returns.

Default Value Initialization Changes the Map

The process of inserting a missing key with a default value is called default value initialization. The default value depends on the type of the value. The source gives the integer case: the default for an integer type is 0. Because the subscript both inserts and returns a value, a lookup of a missing key changes the map as well as producing a result. The diagram below compares the phone book before and after looking up the missing name Ben. The sample entries are illustrative.

Ana4417Ana4417Cara9032Cara9032Ben0
What does the map contain before and after a lookup with a missing key when a default value is supplied, and what new entry appears?
Best practice

Pick the value type and the meaning of its default together. The source's own choice of 0 for an invalid telephone number shows the intent: the default is a meaningful placeholder for the program's design, not a universal constant that means the same thing everywhere.

Tracing Where a Result Diverges

When a lookup returns something surprising, the fastest way to find out why is to trace it. The source points to two questions that locate the divergence: was the key present when the subscript ran, and what was stored under that key at that moment? Checking those two facts usually explains the returned value. The worked example below applies both questions to a lookup that a learner might not expect.

Worked example

Tracing a lookup that returned an unexpected value

The map holds Ana with 4417 and Cara with 9032. A learner calls get_number with Ben and expects an error. The function returns 0 instead. Where did the result diverge from the expectation?

  1. 1
    Write down the map before the lookup

    The map contains two keys, Ana and Cara. Ben is not among them.

  2. 2
    Ask whether the key was present

    At the moment the subscript ran, Ben was absent. This is the point where the expectation and the behavior part ways: the learner assumed absence would signal an error, but the map inserts the key instead.

  3. 3
    Apply default value initialization

    Because Ben was absent, the map creates an entry for Ben and assigns the default value 0 for the integer value type.

  4. 4
    Return the default

    The subscript returns the value it just stored, which is 0.

  5. 5
    Check the map again

    The map now contains Ana, Cara, and Ben. A second lookup for Ben would find the key present and return 0 from storage, which looks identical to the first result.

Result

The divergence occurs at the subscript itself. The returned 0 is the default inserted for a missing key, and the map grew by one entry.

Sentinel Values Versus Stored Values

A default value that the program uses to mean that no real answer exists is one kind of sentinel value. The source's example is 0 used for an invalid telephone number, a placeholder that signals the program's design rather than a genuine stored fact. The danger is that a sentinel and a real stored value can have the same content. The table below compares them. Notice that the value alone may not reveal which one you are looking at.

AspectGenuine stored valueSentinel or default value
How it gets in the mapStored deliberately under a key that was already presentInserted by default value initialization when the key was missing
What it meansA real value the program recorded for that keyA placeholder the program's design treats as not a real answer
ExampleCara maps to 9032Ben maps to 0 after a missing-key lookup
Can the value alone tell them apart?No, if the stored value happens to be 0No, the default 0 looks the same as a stored 0

Genuine stored values versus sentinel defaults

Mistakes Beginners Make with Missing Keys

  • Common mistake

    Assuming a lookup for a missing key leaves the map unchanged

    What it looks like

    After calling get_number with Ben, the learner counts two entries and expects the phone book to still hold two.

    Why it fails

    The subscript both returns a value and changes the map. A missing key causes an entry to be inserted, so the map grows by one.

    Do this instead

    After any lookup that might miss, recount the map and expect one extra entry for each missing key that was looked up.

  • Common mistake

    Treating a 0 result as proof that the key was stored

    What it looks like

    The function returns 0 for Ben, and the learner concludes that Ben's number is 0.

    Why it fails

    A 0 can be a genuine stored value or a default inserted for a missing key. The returned value alone does not reveal which.

    Do this instead

    Check whether the key was present before the subscript ran. That fact, not the returned value, determines what happened.

  • Common mistake

    Choosing a default without deciding what it means

    What it looks like

    A program uses 0 as the default telephone number and later treats every 0 as a real, reachable number.

    Why it fails

    The source presents the default as a meaningful placeholder for the program's design. If its meaning is not decided, the placeholder gets read as data.

    Do this instead

    Pick the value type and the meaning of its default together, and document what the default represents.

  • Common mistake

    Expecting a missing key to be reported rather than filled in

Practice Tracing Lookups

MEDIUM

The map holds Ana with 4417 and Cara with 9032. Trace three calls to get_number in order: first Cara, then Ben, then Ben again. For each call, state whether the key was present, what the function returns, and how many entries the map holds afterward.

Hints
  • Ask first whether the key is present at the moment each subscript runs, not whether it will be present later.
  • The first call to Ben is the one that changes the map. Consider what the second call to Ben finds.
  • Keep a running count of entries, starting from two.

Key Takeaways

  • Subscripting a map uses a key of the first type to find and return the value of the second type stored with that key.
  • When the key is present, the map returns the stored value and does not change.
  • When the key is absent, the map inserts the key with a default value, such as 0 for an integer type, and returns that default, so the map grows by one entry.
  • A default can act as a sentinel placeholder, but its returned value can match a genuine stored value, so trace whether the key was present before the lookup ran.

More concepts starting with D