(漂亮)在 Common Lisp 中打印大对象

(Pretty) Print large objects in Common Lisp

如果我有一个 class 包含例如几个将用矢量填充的槽,则通常会出现问题。如果我想让这个 class 的对象或多或少透明,我会为它实现 print-object。在这里我遇到了问题:

  1. 如果我在一行中打印所有内容,REPL 的启发式算法不足以确定如何在多行中排列可打印部分,导致所有内容都向右移动(参见下面的示例)。
  2. 如果我决定手动将输出分成多行,我会遇到如何正确缩进所有内容的问题,这样如果此对象是另一个对象的一部分,缩进将被保留(请参见下面的示例以获得更清晰的信息).

这是代码。考虑两个 classes:

(defclass foo ()
  ((slot1 :initarg :slot1)
   (slot2 :initarg :slot2)))

(defclass bar ()
  ((foo-slot :initarg :foo)))

我有以下实例:

(defparameter *foo*
  (make-instance 'foo
    :slot1 '(a b c d e f g h i j k l m n o p q r s t u v)
    :slot2 #(1 2 3 4 5 6 7 8)))


(defparameter *bar*
  (make-instance 'bar
    :foo *foo*))

我想看到的,是这样的:

> *bar*
#<BAR
  foo-slot = #<FOO
                slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
                slot2 = #(1 2 3 4 5 6 7 8)>>

案例 1:在一行中打印所有内容

print-object 对这些 class 的定义可以是这样的:

(defmethod print-object ((obj foo) out)
  (with-slots (slot1 slot2) obj
    (print-unreadable-object (obj out :type t)
      (format out "slot1 = ~A slot2 = ~A" slot1 slot2))))

(defmethod print-object ((obj bar) out)
  (with-slots (foo-slot) obj
    (print-unreadable-object (obj out :type t)
      (format out "foo-slot = ~A" foo-slot))))

但是,它们的可打印表示并不理想:

> *foo*
#<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1 2 3 4 5
                                                                      6 7 8)>

> *bar*
#<BAR foo-slot = #<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1
                                                                                       2
                                                                                       3
                                                                                       4
                                                                                       5
                                                                                       6
                                                                                       7
                                                                                       8)>>

案例 2:尝试多行打印

使用多行打印,不知如何控制缩进:

(defmethod print-object ((obj foo) out)
  (with-slots (slot1 slot2) obj
    (print-unreadable-object (obj out :type t)
      (format out "~%~Tslot1 = ~A~%~Tslot2 = ~A" slot1 slot2))))

(defmethod print-object ((obj bar) out)
  (with-slots (foo-slot) obj
    (print-unreadable-object (obj out :type t)
      (format out "~%~Tfoo-slot = ~A" foo-slot))))

因此,*foo* 打印正常,但 *bar* 不是:

> *foo*
#<FOO 
  slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
  slot2 = #(1 2 3 4 5 6 7 8)>
*bar*
#<BAR 
  foo-slot = #<FOO 
  slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
  slot2 = #(1 2 3 4 5 6 7 8)>>

过去我尝试使用 print-indent,但没有成功(我看不到它的任何效果,可能没有正确使用它,SBCL 1.2.14)。

是否有解决这个问题的(最好是简单的)方法?

尝试这样的事情(可能需要更多润色):

(defmethod print-object ((obj foo) out)
  (with-slots (slot1 slot2) obj
    (print-unreadable-object (obj out :type t)
      (format out "~<~:_slot1 = ~A ~:_slot2 = ~A~:>" (list slot1 slot2)))))

(defmethod print-object ((obj bar) out)
  (with-slots (foo-slot) obj
    (print-unreadable-object (obj out :type t)
      (format out "~<~:_foo-slot = ~A~:>" (list foo-slot)))))

它使用~<~:>,它们是逻辑块的格式化操作。然后它使用 ~:_,这是一个条件换行符。你应该阅读 relevant hyperspec section.