読者です 読者をやめる 読者になる 読者になる

きくらげ観察日記

好きなことを、適当に。

Gaucheで構造体を定義する

クラスほど大したことをしなくても良い場合、define-record-typeというマクロを使うことでより簡単な構造体を定義することができます。

(define-record-type point #t #t x y)
(define p (make-point 3 4))
(point? p)  ; => #t
(point-x p) ; => 3
(point-y p) ; => 4

define-record-typeの実体はただのdefine-classで、いい感じのコンストラクタとアクセサを定義してくれる仕様になっています。

しかし、pの型をよく見てみると…

gosh> p
#<point 0x2915a20>

Gaucheでは形名は<>で囲われるお約束になっているので、ちょっと微妙な感じです……

(define-record-type <point> #t #t x y)

これで<point>という名前の構造体を定義できますが……

(define q (make-<point> 1 5))
(<point>? q) ;  => t
(<point>-x q) ; => 1

今度は他の関数の名前が微妙な感じに……。

一応コンストラクタ等の名前を指定することもできます。

(define-record-type <point> make-point point? (x point-x) (y point-y))
(define q (make-point 1 5))
(point? q)  ;  => t
(point-x q) ; => 1

これでとりあえずは動きますが、一々名前指定するのが面倒……。

こんな感じのマクロを書けばいい感じに解決できます。

(define-macro (define-struct name . fields)
  (define (make-accessor prefix . postfixes)
    (string->symbol
     (apply string-append (map x->string `(,prefix ,name ,@postfixes)))))
  (define class-name (make-accessor "<" ">"))
  `(define-record-type class-name
     ,(make-accessor "make-")
     ,(make-accessor "" "?")
     ,@(map (lambda (field-name)
             `(,field-name ,(make-accessor "" "-" field-name))) fields)))



(define-struct person name age pref)

(define taro (make-person "Taro" 20 "Hokkaido"))
(person? taro)     ; => #t
(person-name taro) ; => "Taro"

こんな感じのやつ標準で用意してないんですかね…?
あとはwrite-objectとreader-ctorもいい感じにしてくれるやつがほしい……。metaclassとmacro使ったらいい感じにできそうだけど。