Значения, которые не могут быть прочитаны, печатаются с помощью #<
, см. Sharpsign Less-Than-Sign . Common Lisp не определяет, как ха sh -таблицы должны печататься читабельно (нет синтаксиса читателя для таблиц ха sh):
* (make-hash-table)
#<HASH-TABLE :TEST EQL :COUNT 0 {1006556E53}>
Пример в книге подходит только для использования с примером bank , который был показан ранее и не предназначен для использования в качестве механизма сериализации общего назначения.
Существует много способов напечатать таблицу ha sh и прочитать их обратно, в зависимости от ваших потребностей, но нет представления по умолчанию. Смотрите cl-store для библиотеки, которая хранит произвольные объекты.
Пользовательская dump
функция
Давайте напишем форму, которая оценивает эквивалент га sh -столы; давайте определим обобщенную функцию c с именем dump
и метод по умолчанию, который просто возвращает объект как есть:
(defgeneric dump (object)
(:method (object) object))
При наличии таблицы ha sh мы можем сериализовать ее как plist (последовательность элементов ключ / значение в списке), при этом также вызывая dump
для ключей и значений, в случае, если наши таблицы * sh содержат таблицы * sh:
(defun hash-table-plist-dump (hash &aux plist)
(with-hash-table-iterator (next hash)
(loop
(multiple-value-bind (some key value) (next)
(unless some
(return plist))
(push (dump value) plist)
(push (dump key) plist)))))
Вместо того, чтобы заново изобретать колесо, мы могли бы также использовать hash-table-plist
из системы alexandria .
(ql:quickload :alexandria)
Выше эквивалентно to:
(defun hash-table-plist-dump (hash)
(mapcar #'dump (alexandria:hash-table-plist hash)))
Затем мы можем специализировать dump
для таблиц ha sh:
(defmethod dump ((hash hash-table))
(loop
for (initarg accessor)
in '((:test hash-table-test)
(:size hash-table-size)
(:rehash-size hash-table-rehash-size)
(:rehash-threshold hash-table-rehash-threshold))
collect initarg into args
collect `(quote ,(funcall accessor hash)) into args
finally (return
(alexandria:with-gensyms (h k v)
`(loop
:with ,h := (make-hash-table ,@args)
:for (,k ,v)
:on (list ,@(hash-table-plist-dump hash))
:by #'cddr
:do (setf (gethash ,k ,h) ,v)
:finally (return ,h))))))
Первая часть l oop вычисляет все га sh -табильные свойства, такие как тип используемой функции ha sh или reha sh -size, и строит args
, список аргументов для вызова make-hash-table
с теми же значениями.
В предложении finally
мы строим выражение loop
(см. Обратные кавычки), которое сначала выделяет таблицу ha sh, а затем заполняет ее в соответствии с текущей стоимостью Эс ха sh и, наконец, вернуть новый ха sh. Обратите внимание, что сгенерированный код не зависит от alexandria , его можно прочитать из другой системы Lisp, у которой нет этой зависимости.
Пример
CL-USER> (alexandria:plist-hash-table '("abc" 0 "def" 1 "ghi" 2 "jkl" 3)
:test #'equal)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100C91F8C3}>
Дамп it:
CL-USER> (dump *)
(LOOP :WITH #:H603 := (MAKE-HASH-TABLE :TEST 'EQUAL :SIZE '14 :REHASH-SIZE '1.5
:REHASH-THRESHOLD '1.0)
:FOR (#:K604 #:V605) :ON (LIST "jkl" 3 "ghi" 2 "def" 1 "abc"
0) :BY #'CDDR
:DO (SETF (GETHASH #:K604 #:H603) #:V605)
:FINALLY (RETURN #:H603))
Сгенерированные данные также являются действительным кодом Lisp, оцените его:
CL-USER> (eval *)
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100CD5CE93}>
Полученный ha sh равен equalp
к исходному:
CL-USER> (equalp * ***)
T