Я бы предложил две вещи.Первый - попытаться отследить функцию, которая модифицирует хеш-таблицы.Это (setf gethash)
за исключением того, что gethash
является аксессором , а не функцией .Чтобы выяснить, какая функция используется реализацией для обновления хеш-таблицы, выполните, например,
(disassemble (compile (defun foo (x y) (setf (gethash x y) t))))
, которая показывает код сборки, который должен содержать вызов внутренней функции с именем, подобным puthash
.
Действие (trace puthash)
может привести к проблеме, так как реализация может обновить хэш-таблицу как часть этого вызова.Но если предположить, что это работает, это может показать вам, где происходит неожиданное обновление хеш-таблицы.
Некоторые реализации расширили функциональность trace
.Например, Allegro CL предлагает множество опций трассировки .Например, если ваши функции называются foo
и bar
, и вы хотите видеть вызовы excl::%puthash
из этих функций, вы скажете:
(trace (excl::%puthash :inside (foo bar))
и если вы хотите увидеть, чтоконтекст вызова включает бит стека:
cl-user(1): (trace (excl::%puthash :inside foo :show-stack 3))
(excl::%puthash)
cl-user(2): (setf (gethash 3 (make-hash-table)) 2)
;; Note: no trace output, because not in foo
2
cl-user(3): (foo 3 (make-hash-table))
;; Note: within foo, so trace output which includes 3 stack frames
0[2]: (excl::%puthash 3 #<eql hash-table with 0 entries @ #x100192cfa72> t)
0^ <- (system::..runtime-operation "fwrap_start" 3 #<eql hash-table with 0 entries @ #x100192cfa72> t)
0^ <- (foo 3 #<eql hash-table with 0 entries @ #x100192cfa72>)
0^ <- (eval (foo 3 (make-hash-table)))
0[2]: returned t
t
Трассировка, подобная этой, может быть чрезвычайно полезна при работе с неизвестным кодом.
Второе, что нужно сделать, это проверитькод, чтобы убедиться, что ключи, хранящиеся в хэш-таблице, не изменены.Например,
(let ((x (list 1 2))
(ht (make-hash-table :test 'equal))
(setf (gethash x ht) t)
(setf (car x) 10) ;; not allowed!
(gethash x ht)) ;; now in a funny state: may return T, or nil
См. 18.1.2 Изменение ключей хеш-таблицы .Более того, литеральные объекты также не могут быть изменены, что также может быть причиной странностей:
(let ((x '(1 2)))
(setf (car x) 10) ;; not allowed!