如何在print-object方法中正确使用格式控制参数打印树状CLOS对象
解决方案:递归缩进打印Rope二叉树结构
要实现Rope节点的递归缩进打印,核心是通过动态变量跟踪缩进级别,在递归打印子节点时递增缩进量,同时调整print-object方法的格式输出逻辑。
修改后的完整代码
(defvar *rope-indent* 0 "动态变量,跟踪当前打印的缩进级别,单位为空格数") (defclass rope () ((depth :type fixnum :initform 0 :initarg :depth :reader rope-depth) (weight :type fixnum :initform 0 :initarg :weight :reader rope-weight))) (defclass rope-leaf (rope) ((string :type string :initarg :string :reader rope-string))) (defclass rope-node (rope) ((left :type rope :initarg :left :reader rope-left) (right :type rope :initarg :right :reader rope-right))) (defmethod print-object ((object rope) stream) (print-unreadable-object (object stream :type t :identity nil) ; 输出当前缩进量对应的空格 (format stream "~v@t" *rope-indent*) (with-slots (depth weight) object (format stream "depth: ~a weight: ~a" depth weight)))) (defmethod print-object ((object rope-node) stream) (call-next-method) (with-slots (left right) object ; 换行后打印左子节点,缩进+2 (format stream "~%") (let ((*rope-indent* (+ *rope-indent* 2))) (format stream "left: ~a" left)) ; 换行后打印右子节点,缩进+2 (format stream "~%") (let ((*rope-indent* (+ *rope-indent* 2))) (format stream "right: ~a" right)))) (defmethod print-object ((object rope-leaf) stream) (print-unreadable-object (object stream :type t :identity nil) (format stream "~v@t" *rope-indent*) (with-slots (depth weight string) object (format stream "depth: ~a weight: ~a ~s" depth weight string))))
实现原理
动态变量传递缩进状态
使用*rope-indent*动态变量记录当前需要缩进的空格数,每次递归打印子节点时,通过let绑定将缩进量增加2,递归结束后自动恢复原缩进值(利用动态变量的作用域特性)。格式指令控制缩进输出
使用format的~v@t指令:~v表示取后续参数的值作为格式参数@t表示输出指定数量的空格实现对齐
配合*rope-indent*的值,就能在每个节点前输出对应长度的缩进。
调整
print-unreadable-object行为
添加:identity nil参数,避免打印对象的内存地址,专注于展示结构信息;同时确保每个节点的输出在独立行,配合缩进形成清晰的树状层级。
测试示例
(let* ((leaf1 (make-instance 'rope-leaf :depth 1 :weight 5 :string "hello")) (leaf2 (make-instance 'rope-leaf :depth 1 :weight 5 :string "world")) (node (make-instance 'rope-node :depth 2 :weight 5 :left leaf1 :right leaf2))) (print node))
预期输出
#<ROPE-NODE depth: 2 weight: 5 left: #<ROPE-LEAF depth: 1 weight: 5 "hello"> right: #<ROPE-LEAF depth: 1 weight: 5 "world">>
原代码问题分析
- 缺少缩进状态跟踪:递归打印子节点时没有传递缩进信息,导致所有节点在同一层级输出。
- 格式指令使用错误:原代码中
~<~%left: ~a ~%right: ~a~>需要接收一个列表参数(正确用法是(format stream "~<~%left: ~a ~%right: ~a~>" (list left right))),但即使修正也无法解决缩进问题,核心还是没有跟踪缩进级别。
内容的提问来源于stack exchange,提问作者BitTickler
相关产品推荐
相关产品推荐

