Solution (source code)

= Solution

Search for the predecessor of the matching <linked list node>, or for the last <linked list node> if there is no match. This helper makes deletion uniform:
``
class LinkedBook extends Book {
  private val header = new Node("", "", null)

  // Pre: name != null.
  // Post: result is header or a real node.
  // All real nodes through result have names different from name.
  // result.next is null or has name equal to name.
  // No state is changed.
  private def findPrev(name: String): Node = {
    require(name != null)
    var p = header
    while (p.next != null && p.next.name != name) {
      p = p.next
    }
    p
  }

  def store(name: String, number: String): Unit = {
    require(name != null && number != null)
    val p = findPrev(name)
    if (p.next == null)
      p.next = new Node(name, number, null)
    else
      p.next.number = number
  }

  def recall(name: String): String = {
    require(name != null)
    val p = findPrev(name)
    require(p.next != null, "Name is not in the book")
    p.next.number
  }

  def isInBook(name: String): Boolean = {
    require(name != null)
    findPrev(name).next != null
  }

  def delete(name: String): Boolean = {
    require(name != null)
    val p = findPrev(name)
    if (p.next == null) false
    else {
      p.next = p.next.next
      true
    }
  }
}
``
The search <loop invariant> says that `p` is the header or a reachable real <linked list node>, and every real <linked list node> from the start through `p` has a name different from the requested name. The <linked list> has not been changed. <Short-circuit evaluation> protects `p.next.name` when the end is reached. The <loop variant> is the number of real <linked list nodes> strictly after `p`; finiteness and acyclicity make it a nonnegative decreasing <integer>. Consequently the helper's stated <postcondition> holds.

For `store`, a found name changes only its number; an absent name adds exactly one fresh binding at the end, maintaining uniqueness. For `delete`, unlinking the found <linked list node> removes exactly the requested binding; absence returns false without mutation. Both query operations preserve the <abstraction function>. Non-null <preconditions> are checked at entry; \b[`recall` additionally checks membership using the one completed search], avoiding a redundant whole-list membership scan. The helper is the only additional operation and its complete behavioral contract is stated above.