HOME もうひとつの Scheme 入門 download 書き込む

14. ベクトルと構造体

1. 初めに

今回はベクトルと構造体について説明します。ベクトルは整数によってインデックスされたひとまとまりの データで、C 言語の配列に相当しますが、格納するデータのデータ型が同じである 必要はありません。

一般にベクトルは同じ長さのリストよりも空間効率が高く、ランダムに選択した要素に対する 平均アクセス時間もリストより早くなります。 一方、ベクトルの操作は副作用が伴うため、不用意に使うとバグの元になります。

また、構造体も C 言語の構造体と同様のものです。ただ、Lisp 語族にはマクロがあるので、 値にアクセスしたり、値をセットする関数を自動で書いてくれます。

2. ベクトルの例

Scheme ではベクトルは #(1 2 3) というようにリストの前に # を付けて表します。 リテラルとして使うときはリスト同様クォートしなければなりません。

例:

'#(1 2 3)             ; 整数のベクトル
'#(a 0 #\a)           ; シンボル、整数、文字を要素に持つベクトル

3. ベクトルにかかわる関数

R6RS には以下に示す関数が定義されています。
(vector arg1 arg2 ... )
args からなるベクトルを作成して返します。
(vector 'a 'b 'c) ⇒ #(a b c)
(vector? obj)
obj がベクトルなら #t、そうでなければ #f を返す。
(make-vector k)
(make-vector k fill)
k 個の要素を保持したベクトルを新たに割り当ててそれを返す。 2番目の引数 fill が与えられた場合、各要素は fill で初期化される。
(vector obj ...)
指定した引数を要素とするベクトルを新たに割り当てそれを返す。
(vector-length vector)
vector に含まれる要素数を返す。
(vector-ref vector k)
vectork 番目の要素を返す。
(vector-set! vector k obj)
vectork 番目の要素を obj にする。
(vector->list vector)
vector をリストに変換する。
(list->vector list)
list をベクトルに変換する。
(vector-fill! vector fill)
vector の全要素を fill にする。
(vector-map proc vec1 vec2 ...)
map と同様の関数です。引数に、リストの代わりにベクトルを取ります。
(vector-for-each proc vec1 vec2 ...)
for-each と同様の関数です。引数に、リストの代わりにベクトルを取ります。
例:ベクトルの和を求める関数
(define (add-vector v1 v2)
  (vector-map + v1 v2))
問題1:
2つのベクトルの内積を求める関数を書いてください。

4. 構造体

R6RS ではライブラリで構造体が規定されています。 R6RS の構造体は、かなり大掛かりなものなので、ここでは簡単な使い方のみを説明します。

構造体は define-record-type で定義します。 例を示しながら説明したほうがわかりやすいので本を例にとって説明します。 本の属性としては、

があります。本を表す構造体 book は以下の様に定義します。
(define-record-type book (fields title authors publisher year isbn))
"The Cathedral and Bazaar" を登録するときは以下のようにします。make-book という関数が
(define bazaar 
  (make-book 
   "The Cathedral and the Bazaar"
   "Eric S. Raymond"
   "O'Reilly"
   1999
   0596001088))
構造体を生成する関数はカスタマイズすることもできます。 例えば、前の定義だと、属性と値の対応がわかりづらいので、 スロット名と値の対を与えて構造体を生成するようにします。

下の例では、protocol オプションを使って、構造体を生成する関数をカスタマイズします。 引数を読み込んで、初期値をいったんベクトルに保存し、そのベクトルを使って構造体を初期化します。

(define-record-type book2
                    (fields title authors publisher year isbn)
                    (protocol
                     (lambda (p)
                       (lambda ls
                         (let ((len (length ls)))
                           (if (and (even? len) (<= len 10))
                             (let ((arg (make-vector 5 "")))
                               (let loop ((ls ls))
                                 (if (null? ls)
                                     (p (vector-ref arg 0)
                                        (vector-ref arg 1)
                                        (vector-ref arg 2)
                                        (vector-ref arg 3)
                                        (vector-ref arg 4))
                                   (begin
                                    (vector-set! arg
                                                 (case (car ls)
                                                   ((title) 0)
                                                   ((authors) 1)
                                                   ((publisher) 2)
                                                   ((year) 3)
                                                   ((isbn) 4))
                                                 (cadr ls))
                                     (loop (cddr ls))))))
                             'wrong-argument--cannot-make-book2))))))

(define bazaar 
  (make-book2 
   'title "The Cathedral and the Bazaar"
   'authors "Eric S. Raymond"
   'publisher "O'Reilly"
   'year 1999
   'isbn 0596001088))