第8章「構造体」

概要

ANSI Common Lispの第8章「構造体」を説明します。

構造体とは

ANSI Common Lispの構造体( structure )はクラスによく似ていますが、以下の特徴を持っています。
  • 構造体はクラスよりも簡潔です。スロットを指定する以外のことはほぼ全て自動で行われます。
  • 構造体はクラスよりも高速です。多くの場合はベクトルで実装されるので、スロットへのアクセスも高速です。
  • 構造体はクラスよりも拡張性が乏しいです。スロットを受け継ぐことはできますが、完全な継承はできません。
構造体にも様々な機能が備わっていますが、基本的には簡潔かつ高速な利用の場合に構造体を用いるため、このページでは簡潔なデータ構造としての用例(オプションを使用せず、デフォルトの方法で使う場合)のみを示します。

サンプル: 構造体の全て

ANSI Common Lispの第8章で定められているオペレータはdefstructマクロとcopy-structure関数だけです。この点からも、構造体はデフォルトの設定で使うものということが分かると思います。

デフォルトでの利用を基本とすれば、構造体の全ては非常に小さいサンプルで示すことができます。
;; 定義
(defstruct datetime
  year month date hour minute second)
; => DATETIME

;; 生成
(defparameter now (make-datetime
                   :year 2017
                   :month 11
                   :date 3
                   :hour 21
                   :minute 49
                   :second 50))
; => NOW

now
; => #S(DATETIME :YEAR 2017 :MONTH 11 :DATE 3 :HOUR 21 :MINUTE 49 :SECOND 50)

;; アクセッサ関数(読み込み)
(datetime-year now)
; => 2017

;; アクセッサ関数(書き込み = (setf ...)関数) 
(setf (datetime-month now) 10)
; => 10

;; 述語関数
(datetime-p now)
; => T

;; コピー関数
(defparameter now2 (copy-datetime now))
; => NOW2

;; 型
(typep now2 'datetime)
; => T

defstructマクロ: 構造体の定義

構造体はdefstructマクロから始まります。このマクロはANSI Common Lispのマクロの中でも最も働き者のマクロの一つです。このマクロの主な仕事を以下に列挙します。(structure1を構造体の名前、slot1をスロットの名前とします。)
  1. 構造体生成関数の定義。 make-structure1という名前の構造体生成関数を自動で作ります。構造体生成関数はmake-instance関数のようにスロットの初期値を設定できます。
  2. アクセッサ関数の定義(読み込み・書き込み)。 structure1-slot1という名前のアクセッサ関数を定義します。このアクセッサ関数は読み込みも書き込みも可能で、setfマクロの place 部分で使うことで書き込みに対応します。
  3. 述語関数の定義。 structre1-pという名前の構造体判定関数を定義します。この関数を使えばオブジェクトがstructure1構造体かどうかを判定できます。
  4. コピー関数の定義。 copy-structre1という名前のコピー用関数を定義します。構造体のスロットも含めて内容をコピーできます。
  5. 型の定義。 structure1という型を定義します。この型はtypep関数で使用することができます。
これが前節のサンプルで示した構造体の全てです。defstructマクロはdefclassマクロに比べて自動化されているため、ほとんど何も指定しなくても構造体を使いこなせるような関数が自動的に定義されます。
defstructマクロの構文はdefclassと似ていますが、スロットの指定をリストではなく body として書く点が異なります。例えば、x, y, zという3つのスロットを持つデータ構造を構造体とクラスでそれぞれ定義すると以下のようになります。
;;; 構造体による定義例
(defstruct three-struct
  x
  y
  z)
; => THREE-STRUCT

;;; クラスによる定義
(defclass three-class ()
  (x
   y
   z))
; => #<STANDARD-CLASS THREE-CLASS>

構造体はスロットをそのまま列挙するのに対して、クラスはリストにしている点が異なります。また、定義名の次にクラスはスーパークラスのリストが来るのに対して、構造体は何もありません。もちろん、クラスの場合は:accessorでアクセッサ関数を指定したらアクセッサ関数が定義されますが、何も定義しないとslot-valueでのみアクセス可能です。他方、構造体の場合は何も指定しなくもてもアクセッサ関数が定義されます。

デフォルト値とtypeオプション

defstructは何度も述べている通りオプションを使わずに使うのが一番便利なのですが、スロットのデフォルト値と型の指定は使うことがあるので覚えておいた方がいいでしょう。
例えば、先ほどのthree-struct構造体にデフォルト値と型を指定する場合は以下のようになります。
(defstruct three-struct
  (x 0 :type fixnum)
  (y "" :type string)
  z)
; => THREE-STRUCT

(make-three-struct)
; => #S(THREE-STRUCT :X 0 :Y "" :Z NIL)

スロットの定義の形式は一般に以下の3通りです。(なお、スロットのオプションとしては:read-onlyも利用できますが、ここでは型の指定に限定しています。)
  1. シンボル
  2. リスト
    • シンボルとデフォルト値を要素とする2要素のリスト
    • シンボル、デフォルト値、:type、型指定子を要素とする4要素のリスト
上の例で分かる通り、デフォルト値を設定しない場合はnilが使われます。また、デフォルト値の設定をせずに型を指定することはできません。

0 件のコメント :

コメントを投稿