Common Lisp中是否可以使用以下工具来调试条件?
有一个被修改的哈希表(键的值之一变为NIL),我正在尝试对其进行调试。我能够将其范围缩小到可能发生的地方,但是该表并没有通过官方访问器在那里明确修改。
我无法发布代码,因为它是专有的。
答案 0 :(得分:2)
我建议两件事。第一种是尝试跟踪修改哈希表的函数。那就是(setf gethash)
,除了gethash
是访问者而不是功能。要找出实现使用哪个函数来更新哈希表,请执行例如
(disassemble (compile (defun foo (x y) (setf (gethash x y) t))))
其中显示了汇编代码,其中应包含对内部函数的调用,其名称类似于puthash
。
(trace puthash)
的操作可能会导致问题,因为实现可能会在此调用中更新哈希表。但是假设它可行,它可能会向您显示意外的哈希表更新发生的位置。
某些实现扩展了trace
的功能。例如,Allegro CL报价many trace options。例如。如果您的函数分别称为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 Modifying Hash Table Keys。 此外,literal objects可能也不会被修改,这也可能引起怪异:
(let ((x '(1 2)))
(setf (car x) 10) ;; not allowed!