Telephone Lookup
July 23, 2013
We begin by pairing the names with their rank, which we will use in place of the telephone number:
(define surnames (map list (map string-upcase (map symbol->string '(
SMITH JOHNSON ... VANG))) (range 1 1001)))
The code that signs a name just looks up each character and returns a list:
(define (sign name)
; telephone keyboard A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
(define keys (vector 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 7 8 8 8 9 9 9 9))
(define (lookup c) (vector-ref keys (- (char->integer c) 65)))
(map lookup (string->list name)))
To create the database, we initialize a hash table and add each name/rank pair to it:
(define t (make-hash))
(do ((ss surnames (cdr ss))) ((null? ss))
(set! t (t 'update (sign (caar ss))
(lambda (k v) (cons (car ss) v))
(list (car ss)))))
Then looking up a name by its telephone-keypad signature is simple:
(define (lookup name)
(cdr (t 'lookup (sign name))))
We used range
and string-upcase
from the Standard Prelude and our new hash tables from a recent exercise. You can run the program at http://programmingpraxis.codepad.org/ocKGx9nn.
Collisions aren’t a problem, as we suggested earlier. Out of a thousand surnames, there are only 19 pairs that have the same signature, and one triple: BARR, BASS, CARR. Lesk would have had even fewer collisions, because he added a first initial that we don’t have here; he reported only about 0.2% collisions. (I’m quoting from Column 2, Exercise 6 of Jon Bentley’s book Programming Pearls.)
Here is my python solution, I didn’t try running it but I believe it is mostly correct:
The problem says “Your task is to write a program that takes a name and returns a telephone number”. However, it seemed more fitting to write a program that takes a signature and returns a telephone number, as opposed to just taking a name. If the program took the name as input, then there would seemingly be no need for the name’s signature.
Here’s a Common Lisp program that takes a name signature and returns associated names and phone numbers. The database only contains a few names, including an example collision.
I have a feeling a lot could be simplified, but it’s quite late already.
This code runs on chicken scheme, where srfi-1 needs to be included explicitly, and srfi-26 (cut, cute) isprovided by default.
Here’s a ruby version …
I just used a few of the name to show the technique. The only slightly tricky part is the inject. We need to initialize it to an empty hash and then when we add the name to the hash if there’s nothing currently there, we initialize with an empty array ( the ||= [] piece).
I’ve missed doing these … I’m hoping to get back into them a bit.
A Python solution: