構造体要素をカンマ区切りで出力する


Tag: 【募集中】

構造体というかオブジェクト全般。

オブジェクトの文字列プリントを司るのは print-object。 CLOSの理解を必要としますが、見ていればわかるでしょう。

 (defstruct my-struct ....)
 (defmethod print-object (s (o my-struct))
   ...)

s は出力先のストリーム。 print-object を直に呼ぶことは推奨されていません。ANSI CL そのため、こうなります。

実際には print の中で print-object が呼ばれます。 このときには、*standard-output* が s に代入されて実行されます。

さて、次は要素をカンマ区切りで出力する方法です。 これには MOP (metaobject protocol) を使います。 クラスの内部情報を見るためのプロトコルです。 (quicklispインストール済みを前提)

 (require :closer-mop)
 (defmethod print-object ((o my-struct) s)
   (let ((names (mapcar #'slot-definition-name (class-slots (class-of o)))))
     (let ((first t))
       (dolist (name names)
         (if first
             (setf first nil)
             (write-char #\, s))
         (princ name s)))))

大事なのはこの部分ですね:

 (mapcar #'slot-definition-name (class-slots (class-of o)))

slot-definition-name と class-slots の ふたつの関数は、ANSIでは定義されていません。 AMOP に載っています。

動くコードをまとめると:

 (in-package :cl-user)
 (require :closer-mop)
 (use-package :closer-mop)
 (defstruct my-struct
   (x 1)
   (y 0)
   (z 0))
 (defmethod print-object ((o my-struct) s)
   (let ((names (mapcar #'slot-definition-name (class-slots (class-of o)))))
     (let ((first t))
       (dolist (name names)
         (if first
             (setf first nil)
             (write-char #\, s))
         (princ name s)))))
 (print (make-instance 'my-struct))