|
7 | 7 | (declaim (optimize (speed 3) (debug 0) (safety 0))) |
8 | 8 | (setq *block-compile-default* t) |
9 | 9 |
|
10 | | -(declaim (inline nth-digit char->digit)) |
| 10 | +(declaim (inline nth-digit char->digit digitp)) |
11 | 11 |
|
12 | | -(declaim (ftype (function (string (unsigned-byte 8)) (unsigned-byte 8)) nth-digit)) |
| 12 | +(declaim (ftype (function (simple-string (unsigned-byte 8)) string) nth-digit)) |
13 | 13 | (defun nth-digit (digits i) |
14 | 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))) |
| 15 | + (ecase (char digits i) |
| 16 | + ((#\0) "0") |
| 17 | + ((#\1) "1") |
| 18 | + ((#\2) "2") |
| 19 | + ((#\3) "3") |
| 20 | + ((#\4) "4") |
| 21 | + ((#\5) "5") |
| 22 | + ((#\6) "6") |
| 23 | + ((#\7) "7") |
| 24 | + ((#\8) "8") |
| 25 | + ((#\9) "9"))) |
16 | 26 |
|
17 | | -(declaim (ftype (function (base-char) (unsigned-byte 8)) char->digit)) |
| 27 | +(defmacro scase (s &rest cases) |
| 28 | + (cons 'or (loop for c in cases |
| 29 | + collect (list 'if (list 'string= s (car c)) (cadr c) )))) |
| 30 | + |
| 31 | +(declaim (ftype (simple-string) boolean) digitp) |
| 32 | +(defun digitp (s) |
| 33 | + (and |
| 34 | + (= 1 (length s)) |
| 35 | + (scase s |
| 36 | + ("0" t) |
| 37 | + ("1" t) |
| 38 | + ("2" t) |
| 39 | + ("3" t) |
| 40 | + ("4" t) |
| 41 | + ("5" t) |
| 42 | + ("6" t) |
| 43 | + ("7" t) |
| 44 | + ("8" t) |
| 45 | + ("9" t)))) |
| 46 | + |
| 47 | +(declaim (ftype (function (base-char) simple-string) char->digit)) |
18 | 48 | (defun char->digit (ch) |
19 | 49 | "Convert a character to a digit according to the phone number rules." |
20 | 50 | (ecase (char-downcase ch) |
21 | | - ((#\e) 0) |
22 | | - ((#\j #\n #\q) 1) |
23 | | - ((#\r #\w #\x) 2) |
24 | | - ((#\d #\s #\y) 3) |
25 | | - ((#\f #\t) 4) |
26 | | - ((#\a #\m) 5) |
27 | | - ((#\c #\i #\v) 6) |
28 | | - ((#\b #\k #\u) 7) |
29 | | - ((#\l #\o #\p) 8) |
30 | | - ((#\g #\h #\z) 9))) |
| 51 | + ((#\e) "0") |
| 52 | + ((#\j #\n #\q) "1") |
| 53 | + ((#\r #\w #\x) "2") |
| 54 | + ((#\d #\s #\y) "3") |
| 55 | + ((#\f #\t) "4") |
| 56 | + ((#\a #\m) "5") |
| 57 | + ((#\c #\i #\v) "6") |
| 58 | + ((#\b #\k #\u) "7") |
| 59 | + ((#\l #\o #\p) "8") |
| 60 | + ((#\g #\h #\z) "9"))) |
31 | 61 |
|
32 | | -(declaim (ftype (function (string) integer))) |
| 62 | +(declaim (ftype (function (simple-string) simple-string))) |
33 | 63 | (defun word->number (word) |
34 | 64 | "Translate a word (string) into a phone number, according to the rules." |
35 | | - (let ((n 1)) ; leading zero problem |
36 | | - (declare (type integer n)) |
| 65 | + (let ((n "")) |
37 | 66 | (loop for i from 0 below (length word) |
38 | 67 | for ch = (char word i) do |
39 | | - (when (alpha-char-p ch) (setf n (+ (* 10 n) (char->digit ch))))) |
| 68 | + (when (alpha-char-p ch) (setq n (concatenate 'string n (char->digit ch))))) |
40 | 69 | n)) |
41 | 70 |
|
42 | 71 | (defglobal *dict* nil |
|
66 | 95 | (format t "~a:~{ ~a~}~%" num (reverse words)) |
67 | 96 | (let ((next-iterations |
68 | 97 | (do ((i start (1+ i)) ; var, initial value, increment per iteration |
69 | | - (n 1) |
| 98 | + (n "") |
70 | 99 | (max (length digits)) |
71 | 100 | (result nil)) |
72 | 101 | ((>= i max) result) ; exit condition and return-value |
73 | | - (setq n (+ (* 10 n) (nth-digit digits i))) |
| 102 | + (setq n (concatenate 'string n (nth-digit digits i))) |
74 | 103 | (let ((next-words (gethash n *dict*))) |
75 | 104 | (when next-words (push (list (1+ i) next-words) result)))))) |
76 | 105 | (if next-iterations |
77 | 106 | (loop for (i next-words) in next-iterations do |
78 | 107 | (loop for word in next-words do |
79 | 108 | (print-translations num digits i (cons word words)))) |
80 | | - (when (not (numberp (first words))) |
| 109 | + (when (not (digitp (first words))) |
81 | 110 | (print-translations num digits (+ start 1) |
82 | 111 | (cons (nth-digit digits start) words))))))) |
83 | 112 |
|
84 | 113 | (defun load-dictionary (file size) |
85 | 114 | "Create a hashtable from the file of words (one per line). Takes a hint |
86 | 115 | for the initial hashtable size. Each key is the phone number for a word; |
87 | 116 | each value is a list of words with that phone number." |
88 | | - (let ((table (make-hash-table :test #'eql :size size))) |
| 117 | + (let ((table (make-hash-table :test #'equal :size size))) |
89 | 118 | (with-open-file (in file) |
90 | 119 | (loop for word = (read-line in nil) while word do |
91 | 120 | (push word (gethash (word->number word) table)))) |
|
0 commit comments