Просто угадайте, чего на самом деле может хотеть Билл.
Допустим, он хочет сопоставить некоторые ключи с некоторыми значениями в виде конфигурации в файле.
Вот процедурный способ.
- открыть поток для данных
- прочитать его как s-выражение
- просмотреть данные и заполнить хеш-таблицу
Пример кода:
(defun read-mapping (&optional (stream *standard-input*))
(destructuring-bind (type &rest mappings) (read stream)
(assert (eq type 'mapping))
(let ((table (make-hash-table)))
(loop for (key value) in mappings
do (setf (gethash key table) value))
table)))
(defun load-config ()
(read-mapping))
(defun load-test-config ()
(with-input-from-string (*standard-input* "(mapping (fred 3) (barney 5))")
(load-config)))
(load-test-config)
Использование:
CL-USER 57 > (load-test-config)
#<EQL Hash Table{2} 402000151B>
CL-USER 58 > (describe *)
#<EQL Hash Table{2} 402000151B> is a HASH-TABLE
BARNEY 5
FRED 3
Преимущества:
- без макросов
- данные не кодируются в исходном кодеи сгенерированный исходный код
- без оценки (безопасность!) через EVAL необходимо
- без раздувания объектного кода через макросы, которые расширяются до большего кода
- функциональная абстракция
- намного проще для понимания и отладки
В качестве альтернативы я написал бы макрос для чтения для {
такой, что {(fred 3) (barney 5)}
будет непосредственно считываться как хеш-таблица.
Если вы хотите иметь вычисляемые значения:
(defun make-table (mappings &aux (table (make-hash-table)))
(loop for (key value) in mappings
do (setf (gethash key table) (eval value)))
table)
CL-USER 66> (describe (make-table '((fred (- 10 7)) (barney (- 10 5)))))
#<EQL Hash Table{2} 4020000A4B> is a HASH-TABLE
BARNEY 5
FRED 3
Превращение в макрос:
(defmacro defmapping (&body mappings)
`(make-table ',mappings))
(defmapping
(fred 3)
(barney 5))