はじめに
最近「ANSI Common Lisp ポール・グレアム著」を読んでいます.この本で構造体について学習したことをメモしておきます.
表示関数を自分で定義
defstruct を使用して構造体を定義する際に,構造体の内容をどのように表示するかを定義した関数を引数に渡すことにより,構造体の内容の表示方法を任意に指定することができます.これを省略すると構造体の内容は,デフォルトの表示方法で表示されます.下のコードによって違いを示します.
;;; デフォルトの表示
(defstruct human
(height 0)
(weight 0))
(make-human :height 160 :weight 40) ;=> #S(HUMAN :HEIGHT 160 :WEIGHT 40)
;;; 自分で定義
;; humanという構造体を重複して定義することによって引き起こるエラーを防ぐための処理
(unintern 'human)
(defstruct (human (:print-function print-human))
(height 0)
(weight 0))
(defun print-human (human stream depth)
;; #< マクロで始まるものは,readで読み込むとエラーになる.
(format stream "#<~Acm, ~Akg>" (human-height human) (human-weight human)))
(make-human :height 160 :weight 40) ;=> #<160cm, 40kg>
このコードにある print-human という関数により,表示方法を任意に指定することが可能になります.ですが depth という引数を使用していないので定義するときに警告が出ます.これを防ぐための方法としてまず考えることができるのは, depth という変数を使用しないと宣言することです.コードを下に示します.
(defun print-human (human stream depth)
(declare (ignore depth))
(format stream "#<~Acm, ~Akg>" (human-height human) (human-weight human)))
ですがこれを毎回書くのは面倒です.そういった時には defstruct のキーワード引数 :print-function の代わりに, :print-object を使うといいみたいです.コードを下に示します.
(defstruct (human (:print-object print-human))
(height 0)
(weight 0))
(defun print-human (human stream)
(format stream "#<~Acm, ~Akg>" (human-height human) (human-weight human)))
(make-human :height 160 :weight 40) ;=> #<160cm, 40kg>
補足
コード中に #< という read で読み込まれるとエラーになるマクロが出てきました.こういった形式で表示するために print-unreadable-object というマクロがあるようです.先程の print-human をこのマクロを使って書き換えてみます.
(defun print-human (human stream)
(print-unreadable-object (human stream)
(format stream "~Acm, ~Akg" (human-height human) (human-weight human))))