|
4 | 4 | ;; each phone number can be expressed as a list of words. |
5 | 5 | ;; Run: (main "word-list-file-name" "phone-number-file-name") |
6 | 6 |
|
| 7 | +(declaim (optimize (speed 3) (debug 0) (safety 0))) |
| 8 | + |
7 | 9 | (defglobal *dict* nil |
8 | 10 | "A hash table mapping a phone number (integer) to a list of words from the |
9 | 11 | input dictionary that produce that number.") |
10 | 12 |
|
| 13 | +(defun nth-digit (digits i) |
| 14 | + "The i-th element of a character string of digits, as an integer 0 to 9." |
| 15 | + (- (char-code (char digits i)) #.(char-code #\0))) |
| 16 | + |
| 17 | +(defun char->digit (ch) |
| 18 | + "Convert a character to a digit according to the phone number rules." |
| 19 | + (ecase (char-downcase ch) |
| 20 | + ((#\e) 0) |
| 21 | + ((#\j #\n #\q) 1) |
| 22 | + ((#\r #\w #\x) 2) |
| 23 | + ((#\d #\s #\y) 3) |
| 24 | + ((#\f #\t) 4) |
| 25 | + ((#\a #\m) 5) |
| 26 | + ((#\c #\i #\v) 6) |
| 27 | + ((#\b #\k #\u) 7) |
| 28 | + ((#\l #\o #\p) 8) |
| 29 | + ((#\g #\h #\z) 9))) |
| 30 | + |
11 | 31 | (defun choose-handler (print-or-count) |
12 | 32 | "Chooses a solution handler function. |
13 | 33 | When a numbers file is fully consumed, call the function with nil words to signal EOF." |
|
24 | 44 | (format t "~a~%" count) |
25 | 45 | (setf count 0)))))) ;; reset count for the next file |
26 | 46 | (t (error "Unknown option: ~a" print-or-count)))) |
27 | | - |
28 | | - |
29 | | -(defun main (&optional print-or-count (dict "tests/words.txt") (nums "tests/numbers.txt") (dict-size 100)) |
30 | | - "Read the input file ¨DICT and load it into *dict*. Then for each line in |
31 | | - NUMS, print all the translations of the number into a sequence of words, |
32 | | - according to the rules of translation." |
33 | | - (let ((handler (choose-handler print-or-count))) |
34 | | - (setf *dict* (load-dictionary dict dict-size)) |
35 | | - (with-open-file (in nums) |
36 | | - (loop for num = (read-line in nil) while num do |
37 | | - (find-translations handler num (remove-if-not #'digit-char-p num)))) |
38 | | - (funcall handler "" nil))) |
39 | 47 |
|
40 | 48 | (defun find-translations (handler num digits &optional (start 0) (words nil)) |
41 | 49 | "Print each possible translation of NUM into a string of words. DIGITS |
|
68 | 76 | (find-translations handler num digits (+ start 1) |
69 | 77 | (cons (nth-digit digits start) words)))))) |
70 | 78 |
|
| 79 | +(defun word->number (word) |
| 80 | + "Translate a word (string) into a phone number, according to the rules." |
| 81 | + (let ((n 1)) ; leading zero problem |
| 82 | + (loop for i from 0 below (length word) |
| 83 | + for ch = (char word i) do |
| 84 | + (when (alpha-char-p ch) (setf n (+ (* 10 n) (char->digit ch))))) |
| 85 | + n)) |
| 86 | + |
71 | 87 | (defun load-dictionary (file size) |
72 | 88 | "Create a hashtable from the file of words (one per line). Takes a hint |
73 | 89 | for the initial hashtable size. Each key is the phone number for a word; |
|
78 | 94 | (push word (gethash (word->number word) table)))) |
79 | 95 | table)) |
80 | 96 |
|
81 | | -(defun word->number (word) |
82 | | - "Translate a word (string) into a phone number, according to the rules." |
83 | | - (let ((n 1)) ; leading zero problem |
84 | | - (loop for i from 0 below (length word) |
85 | | - for ch = (char word i) do |
86 | | - (when (alpha-char-p ch) (setf n (+ (* 10 n) (char->digit ch))))) |
87 | | - n)) |
88 | | - |
89 | | -(defun nth-digit (digits i) |
90 | | - "The i-th element of a character string of digits, as an integer 0 to 9." |
91 | | - (- (char-code (char digits i)) #.(char-code #\0))) |
92 | | - |
93 | | -(defun char->digit (ch) |
94 | | - "Convert a character to a digit according to the phone number rules." |
95 | | - (ecase (char-downcase ch) |
96 | | - ((#\e) 0) |
97 | | - ((#\j #\n #\q) 1) |
98 | | - ((#\r #\w #\x) 2) |
99 | | - ((#\d #\s #\y) 3) |
100 | | - ((#\f #\t) 4) |
101 | | - ((#\a #\m) 5) |
102 | | - ((#\c #\i #\v) 6) |
103 | | - ((#\b #\k #\u) 7) |
104 | | - ((#\l #\o #\p) 8) |
105 | | - ((#\g #\h #\z) 9))) |
| 97 | +(defun main (&optional print-or-count (dict "tests/words.txt") (nums "tests/numbers.txt") (dict-size 100)) |
| 98 | + "Read the input file ¨DICT and load it into *dict*. Then for each line in |
| 99 | + NUMS, print all the translations of the number into a sequence of words, |
| 100 | + according to the rules of translation." |
| 101 | + (let ((handler (choose-handler print-or-count))) |
| 102 | + (setf *dict* (load-dictionary dict dict-size)) |
| 103 | + (with-open-file (in nums) |
| 104 | + (loop for num = (read-line in nil) while num do |
| 105 | + (find-translations handler num (remove-if-not #'digit-char-p num)))) |
| 106 | + (funcall handler "" nil))) |
106 | 107 |
|
107 | 108 | (apply #'main (cdr sb-ext:*posix-argv*)) |
0 commit comments