Common Lisp中结构的:print-function是否干扰*print-readably*?
:print-function对*print-readably*的影响 我尝试将Common Lisp结构可读打印到文件中,以便后续可以读取回来。发现SBCL具备相当完善的内置复杂对象可读打印机制,或许无需编写专门的print-object方法。
请问我为结构自定义的:print-function是否会干扰*print-readably*的功能?
以下是我的结构定义:
(defstruct (problem-state (:conc-name problem-state.) (:print-function print-problem-state) (:copier nil)) "A planning state including the current propositional database." (name nil :type symbol) ;last action executed (instantiations nil :type list) ;from last action effect (happenings nil :type list) ;a list of (object (next-index next-time next-direction)) pairs (time 0.0 :type real) (value 0.0 :type real) (heuristic 0.0 :type real) (idb (make-hash-table) :type hash-table) ;integer hash table of propositions (hidb (make-hash-table) :type hash-table)) ;integer table for happening events
执行以下保存函数时:
(defun save (object file) "Saves an object to a file so it can be read in later." (with-open-file (out-stream file :direction :output) (let ((*print-readably* t)) (pprint object out-stream))))
生成的结构实例输出是人类可读的,但无法被Lisp读取。若问题确实出在:print-function,我是否需要禁用它?
是的,你自定义的:print-function确实会干扰*print-readably*的功能。
当*print-readably*被设为t时,Common Lisp的打印系统会优先确保输出能被read函数正确解析,生成符合Lisp语法的表达式。但自定义的:print-function通常是为了生成人类友好的格式,而非机器可读取的语法结构,这就导致你保存的内容无法被正确读回。
解决办法有两种:
1. 修改自定义打印函数,适配*print-readably*
在你的print-problem-state函数里,先判断*print-readably*的状态:如果为t,就生成符合结构可读格式的输出(用#S(...)语法);否则保持原来的人类可读格式。示例如下:
(defun print-problem-state (object stream depth) (declare (ignore depth)) ; 不需要depth参数可忽略 (if *print-readably* ;; 生成可被read解析的结构语法 (format stream "#S(PROBLEM-STATE :NAME ~S :INSTANTIATIONS ~S :HAPPENINGS ~S :TIME ~S :VALUE ~S :HEURISTIC ~S :IDB ~S :HIDB ~S)" (problem-state.name object) (problem-state.instantiations object) (problem-state.happenings object) (problem-state.time object) (problem-state.value object) (problem-state.heuristic object) (hash-table-to-alist (problem-state.idb object)) (hash-table-to-alist (problem-state.hidb object))) ;; 原来的人类可读打印逻辑 (format stream "Problem State: ~A (Time: ~F, Value: ~F, Heuristic: ~F)" (problem-state.name object) (problem-state.time object) (problem-state.value object) (problem-state.heuristic object))))
这里要注意:默认情况下哈希表无法被read正确解析,所以需要额外写转换函数,把哈希表转换成关联列表(alist),读取的时候再转换回哈希表:
;; 哈希表转alist (defun hash-table-to-alist (ht) (let ((alist nil)) (maphash (lambda (k v) (push (cons k v) alist)) ht) alist)) ;; alist转哈希表 (defun alist-to-hash-table (alist) (let ((ht (make-hash-table))) (dolist (pair alist ht) (setf (gethash (car pair) ht) (cdr pair)))))
2. 临时禁用自定义打印函数
在保存对象时,动态修改结构的打印函数为默认值,打印完成后恢复。不过这种方法依赖SBCL的实现细节,示例如下:
(defun save (object file) "Saves an object to a file so it can be read in later." (with-open-file (out-stream file :direction :output) (let ((*print-readably* t) ;; 临时替换结构的print-function为默认实现 (original-print-fn (get 'problem-state 'sb-mop:print-function))) (setf (get 'problem-state 'sb-mop:print-function) nil) (unwind-protect (pprint object out-stream) ;; 恢复原来的print-function (setf (get 'problem-state 'sb-mop:print-function) original-print-fn)))))
注意:默认结构打印同样无法处理哈希表的可读问题,所以还是需要配合哈希表的转换逻辑才能完整读回数据。
额外提醒
SBCL的内置可读打印机制确实强大,但对于哈希表这类非标准可读对象,还是需要手动处理转换逻辑,确保保存的内容能被完整读回。
内容的提问来源于stack exchange,提问作者davypough

