(漂亮)在 Common Lisp 中打印大对象
(Pretty) Print large objects in Common Lisp
如果我有一个 class 包含例如几个将用矢量填充的槽,则通常会出现问题。如果我想让这个 class 的对象或多或少透明,我会为它实现 print-object
。在这里我遇到了问题:
- 如果我在一行中打印所有内容,REPL 的启发式算法不足以确定如何在多行中排列可打印部分,导致所有内容都向右移动(参见下面的示例)。
- 如果我决定手动将输出分成多行,我会遇到如何正确缩进所有内容的问题,这样如果此对象是另一个对象的一部分,缩进将被保留(请参见下面的示例以获得更清晰的信息).
这是代码。考虑两个 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.
如果我有一个 class 包含例如几个将用矢量填充的槽,则通常会出现问题。如果我想让这个 class 的对象或多或少透明,我会为它实现 print-object
。在这里我遇到了问题:
- 如果我在一行中打印所有内容,REPL 的启发式算法不足以确定如何在多行中排列可打印部分,导致所有内容都向右移动(参见下面的示例)。
- 如果我决定手动将输出分成多行,我会遇到如何正确缩进所有内容的问题,这样如果此对象是另一个对象的一部分,缩进将被保留(请参见下面的示例以获得更清晰的信息).
这是代码。考虑两个 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.