You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Common Lisp中结构的:print-function是否干扰*print-readably*?

Common Lisp结构可读打印问题:自定义: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 23:42:37