構造体要素をカンマ区切りで出力する - lisp-cookbook-ja/common-lisp GitHub Wiki
構造体というかオブジェクト全般。
オブジェクトの文字列プリントを司るのは print-object。 CLOSの理解を必要としますが、見ていればわかるでしょう。
(defstruct my-struct ....)
(defmethod print-object (s (o my-struct)) ...)
s は出力先のストリーム。 print-object を直に呼ぶことは推奨されていません。ANSI CL そのため、こうなります。
- NG: (print-object standard-output (make-instance 'my-struct ...))
- OK: (print standard-output (make-instance 'my-struct ...))
実際には 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))