Guile基礎/ベクタ(コアモジュール)

変更履歴

概 要

目 次

参考資料

ベクタの作成

ベクタ定数

literal:
#(datum ... )
datum データ(の外部表現)

この式は datum を成分とするベクタを作ります. datum は何でも指定できます.ただし,次の点に注意して下さい.

具体例 以下の1番目の式は, 数値(10と20),文字(#\a と #\b),文字列("Guile"),シンボル(Scheme)からなるベクタ定数を作っています.2番目の式は数値リスト定数,文字列リスト定数,シンボルリスト定数からなるベクタ定数を作っています.
guile> #(10 20 #\a #\b "Guile" Scheme)
$1 = #(10 20 #\a #\b "Guile" Scheme)
guile> #((1 2 3) ("a" "b" "c") (a b c))
$2 = #((1 2 3) ("a" "b" "c") (a b c))
なお,'Scheme といったようにシンボルにクォート(')をつけると, 以下に示すようにクォート(quote)もデータの一部になってしまいます. リスト定数の場合と同様に,ベクタの成分は評価されないので, 各成分にクォートを付ける必要はありません.
guile> #(10 20 #\a #\b "Guile" 'Scheme)
$3 = #(10 20 #\a #\b "Guile" (quote Scheme))

ベクタ定数の各成分(datum )は評価されません. 例えば,以下の算術式は評価されることはなく, シンボルとしての演算記号,シンボルとしての変数記号(x と y),数値定数からなるリスト定数として処理されます.
guile> #((+ x 20) (- 10 y) (* 10 20))
$4 = #((+ x 20) (- 10 y) (* 10 20))

ベクタ定数(によって生成したベクタデータ)は変更不可能(immutable)なので, その成分を変更しようとするとエラーが発生します.以下の実行例では,10,20,30 の3つの成分からなるベクタ定数の第0成分(先頭の成分)を vector-set! を使って 99 に変更しようとして,エラーが発生しています.
guile> (define vec #(10 20 30))
guile> vec
$1 = #(10 20 30)
guile> (vector-set! vec 0 99)
In procedure vector-set!: Wrong type argument in position 1 (expecting mutable vector): #(10 20 30)

vector ― 要素を指定して作る

procedure:
(vector obj ... )
obj オブジェクト
返り値 ベクタ

この手続きは,obj ... からなるベクタを作って返します. vector 手続きによって作成したベクタは変更可能(mutable)です. つまり,各成分の値を変更することができます.

具体例
guile> (vector 1 2 3)
$1 = #(1 2 3)
guile> (vector 10 #\a "Guile" 'Scheme)
$2 = #(10 #\a "Guile" Scheme)
guile> (vector (+ 10 20) (- 10 20) (* 10 20))
$3 = #(30 -10 200)

vector 手続きで生成したベクタは変更可能(mutable)なので, その成分を変更しようとしてもエラーにはなりません. 以下の実行例は,ベクタ定数の実行例の中でエラーが発生した変更操作を行っています.
guile> (define vec (vector 10 20 30))
guile> vec
$1 = #(10 20 30)
guile> (vector-set! vec 0 99)
guile> vec
$2 = #(99 20 30)

make-vector ― 長さを指定して作る

procedure:
(make-vector len)
(make-vector len obj)
len 長さ(成分数)を表す整数値
obj オブジェクト
返り値 ベクタ

この手続きは,len で指定された長さ(成分数)のベクトルを作って返します.obj を指定した場合,各成分を obj に初期設定します.obj を省略した場合,各成分の値は unspecified です. この手続きで作成したベクタは変更可能(mutable)です.

具体例
guile> (make-vector 3)
$1 = #(#<unspecified> #<unspecified> #<unspecified>)
guile> (make-vector 5 0)
$2 = #(0 0 0 0 0)

list <--> vector ― リストとベクタの相互変換

procedure:
(list->vector lst)
(vector->list vec)
lst リスト
vec ベクタ
返り値 list->vector はベクタを返し,vector->list はリストを返す.

list->vector はリスト(lst)をベクタに変換して返します. 逆に,vector->list はベクタ(vec)をリストに変換して返します. 返り値のベクタやリストは変更可能(mutable)です.

具体例
guile> (list->vector '(10 20 30))
$1 = #(10 20 30)
guile> (vector->list #(10 20 30))
$2 = (10 20 30)

補足 (srfi srfi-43) モジュールをロードすると,機能拡張された同名の手続きを使うことができます.その手続きは,変換対象の部分列が指定できるようになっています.

ベクタの操作

vector? ― ベクタ型の検査

procedure:
(vector? obj)
obj オブジェクト
返り値 ブール値

これは,obj がベクタならば #t を返し, そうでなければ #f を返します.

具体例
guile> (vector? #(10 20 30))
$1 = #t
guile> (vector? '(10 20 30))
$2 = #f

vector-length ― ベクタの長さ(成分数)

procedure:
(vector-length vec)
vec ベクタ
返り値 ベクタの長さ(成分数)

これは vec の長さ(成分数)を返します.

具体例
guile> (vector-length #(10 20))
$1 = 2
guile> (vector-length #(10 20 30))
$2 = 3
guile> (vector-length #(10 20 30 40))
$3 = 4

vector-ref ― 成分の抽出

procedure:
(vector-ref vec idx)
vec ベクタ
idx 添字(成分の番号)
返り値 成分の値

これは vecidx 番目の成分を返します. 次の点に注意して下さい.

具体例
guile> (vector-ref #(10 20 30 40) 0)
$1 = 10
guile> (vector-ref #(10 20 30 40) 1)
$2 = 20
guile> (vector-ref #(10 20 30 40) 2)
$3 = 30
guile> (vector-ref #(10 20 30 40) 3)
$4 = 40
ベクタの長さを $\ell$ とするとき,添字(idx)は $0 \leq$ idx $\leq \ell-1$ を満たさなければいけません. この範囲以外の添字を指定したときにはエラーが発生します.
guile> (vector-ref #(10 20 30 40) 4)
In procedure vector-ref: Argument 2 out of range: 4

vector-set! ― 成分の変更

procedure:
(vector-set! vec idx obj)
vec ベクタ
idx 添字(成分の番号)
obj オブジェクト
返り値 unspecified

これは,ベクタ(vec)の idx 番目の成分を obj に変更します.次の点に注意して下さい.

具体例
guile> (define vec (make-vector 4 'x))
guile> vec
$1 = #(x x x x)
guile> (vector-set! vec 0 'A)
guile> vec
$2 = #(A x x x)
guile> (vector-set! vec 2 (+ 10 20))
guile> vec
$3 = #(A x 30 x)
ベクタが変更可能(mutable)でないときエラーが発生します.
guile> (define vec #(x x x x))
guile> vec
$1 = #(x x x x)
guile> (vector-set! vec 0 'A)
In procedure vector-set!: Wrong type argument in position 1 (expecting mutable vector): #(x x x x)

vector-fill! ― 全成分の変更

procedure:
(vector-fill! vec obj)
(vector-fill! vec obj start)
(vector-fill! vec obj start end)
vec ベクタ
obj オブジェクト
start 変更を開始する位置.省略時は0に設定される.
end 変更を終了する位置.省略時はベクタ(vec)の長さに設定される.
返り値 unspecified

これは,ベクタ(vec)の start 番目から end$\,-1$ 番目までの成分を obj に変更します.これら以外の成分は変更しません.start を省略したときには 0 に設定されます.end を省略したときにはベクタ(vec)の長さ(成分数)に設定されます. 次の点に注意して下さい.

具体例
guile> (define vec (make-vector 4 'x))
guile> vec
$1 = #(x x x x)
guile> (vector-fill! vec 'A)
guile> vec
$2 = #(A A A A)
guile> (vector-fill! vec (+ 10 20) 1 3)
guile> vec
$3 = #(A 30 30 A)
start $=$ end の場合,何も変更しません.
guile> (vector-fill! vec 'Guile 0 0)
guile> vec
$4 = #(A 30 30 A)
guile> (vector-fill! vec 'Guile 1 1)
guile> vec
$5 = #(A 30 30 A)
start(とend)が成分数の場合でもエラーは発生しません.
guile> (vector-fill! vec 'Guile 4)
guile> vec
$6 = #(A 30 30 A)
guile> (vector-fill! vec 'Guile 4 4)
guile> vec
$7 = #(A 30 30 A)

vector-copy ― ベクタのコピー

procedure:
(vector-copy vec)
(vector-copy vec start)
(vector-copy vec start end)
vec ベクタ
start コピーを開始する位置.省略時は0に設定される.
end コピーを終了する位置.省略時はベクタ(vec)の長さに設定される.
返り値 ベクタ
注意

ベクタ(vec)の start 番目から end$\,-1$ 番目までの成分からなるベクタを新たに作成して返します.start を省略したときには 0 に設定されます.end を省略したときにはベクタ(vec)の長さ(成分数)に設定されます. 次の点に注意して下さい.

具体例 以下では,変更不可能(immutable)なベクタ(vec1)を定義して,そのコピー(vec2)を作っています.さらに,vec1 と vec2 が異なるオブジェクトであることを(適当に)確認しています.
guile> (define vec1 #(10 20 30 40))
guile> vec1
$1 = #(10 20 30 40)
guile> (define vec2 (vector-copy vec1))
guile> vec2
$2 = #(10 20 30 40)
guile> (vector-set! vec2 0 99)
guile> vec2
$3 = #(99 20 30 40)
guile> vec1
$4 = #(10 20 30 40)
Guile 3.0.7 以前では,(srfi srfi-43) モジュールをロードすれば startend も指定できる手続きが使えます.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(0 10 20 30 40 50))
guile> vec
$1 = #(0 10 20 30 40 50)
guile> (vector-copy vec 2 5)
$2 = #(20 30 40)
end がベクタ(vec)の長さより大きい場合, エラーは発生せずに start$\,-\,$end の長さのベクタが生成され,コピー元から必要なコピーを行った残りの成分は unspecified になります.
guile> (vector-copy vec 3 10)
$3 = #(30 40 50 #<unspecified> #<unspecified> #<unspecified> #<unspecified>)
start $=$ end だったり, start がベクタの長さだった場合,空ベクタが作成されます.
guile> (vector-copy vec 2 2)
$4 = #()
guile> (vector-copy vec 6 6)
$5 = #()
guile> (vector-copy vec 6)
$6 = #()

参考 Guile 3.0.7 以前では,この手続きは (srfi srfi-43) モジュールの中で次のように定義されています.以下の guile-vector-copy はコアモジュールのコピー手続きです.それから,vector-move-left! はコアモジュールが提供する別種のコピー手続きです.これについては後述する説明を参照して下さい.
(define vector-copy
  (case-lambda*
   ((v) (guile-vector-copy v))
   ((v start)
    (assert-vector v 'vector-copy)
    (let ((len (vector-length v)))
      (assert-valid-start start len 'vector-copy)
      (let ((result (make-vector (- len start))))
        (vector-move-left! v start len result 0)
        result)))
   ((v start end #:optional (fill *unspecified*))
    (assert-vector v 'vector-copy)
    (let ((len (vector-length v)))
      (unless (and (exact-integer? start)
                   (exact-integer? end)
                   (<= 0 start end))
        (error-from 'vector-copy "invalid index range" start end))
      (let ((result (make-vector (- end start) fill)))
        (vector-move-left! v start (min end len) result 0)
        result)))))
この定義を見ると,end がベクタ(vec)の長さより大きい場合の unspcified をオプション引数として任意の値に設定できるようです.実際,次のようになります.
guile> (vector-copy vec 3 10 'X)
$7 = #(30 40 50 X X X X)

補足 Guile[6.6.10.3 Accessing and Modifying Vector Contents] によれば,Guile 3.0.8 のコアモジュールが提供する vector-copy は上述のオプション引数を指定できません.従って,Guile 3.0.8 でも,オプション引数を指定したい場合には (srfi srfi-43) モジュールをロードする必要があります.

vector-copy! ― ベクタの転写

procedure:
(vector-copy! dst at src)
(vector-copy! dst at src start)
(vector-copy! dst at src start end)
dst ベクタ
at ベクタ(dst)の添字.コピーを保存する先頭の位置.
src ベクタ
start ベクタ(src)の添字.コピ―を開始する位置.省略時は0に設定される.
end ベクタ(src)の添字.コピーを終了する位置.省略時はベクタ(src)の長さに設定される.
返り値 unspecified
注意

これは,コピー元のベクタ(src)の start 番目から end$\,-1$ 番目までの成分を,コピー先のベクタ(dst)の at 番目以降の成分に保存(転写)します.次の点に注意して下さい.

補足 この手続きはライブラリ関数の memmove を使って実装しています.カーネルソースの中にある memmove 関数のソースコードを見ると,上で述べたような余計な作業領域を使用しません.上記の説明はあくまで便宜的なものです.

具体例 Guile 3.0.7 以前では,(srfi srfi-43) モジュールをロードすれば vector-copy! 手続きが使えます. 以下では,1つのベクタ(vec)の中で,2番目から5番目の成分(下記の20,30,40,50)を左側(1番目以降の成分)にコピー(転写)する場合を実行してみます.
guile> (use-modules (srfi srfi-43))
guile> (define vec (vector 0 10 20 30 40 50 60 70))
guile> vec
$1 = #(0 10 20 30 40 50 60 70)
guile> (vector-copy! vec 1 vec 2 6)
guile> vec
$2 = #(0 20 30 40 50 50 60 70)
今度は1番目から4番目の成分(下記の10,20,30,40)を右側(2番目以降の成分)にコピー(転写)する場合を実行してみます.
guile> (define vec (vector 0 10 20 30 40 50 60 70))
guile> vec
$3 = #(0 10 20 30 40 50 60 70)
guile> (vector-copy! vec 2 vec 1 5)
guile> vec
$4 = #(0 10 10 20 30 40 60 70)

vector-move-{left!,right!} ― ベクタの転写

procedure:
(vector-move-left! vec1 start1 end1 vec2 start2)
(vector-move-right! vec1 start1 end1 vec2 start2)
vec1 ベクタ
start1 ベクタ(vec1)の添字.コピ―を開始する位置.
end1 ベクタ(vec1)の添字.コピーを終了する位置.
vec2 ベクタ
start2 ベクタ(vec2)の添字.コピーを保存する先頭の位置.
返り値 unspecified

これらは, コピー元のベクタ(vec1)の start1 番目から end1$\,-1$ 番目までの成分を,コピー先のベクタ(vec2)の start2 番目以降の成分に保存(転写)します.

vector-move-left! は,vec1 の添字の小さいほうから順に(直観的に言うと,左側の成分から順に)コピーします.vector-move-right! は,vec1 の添字の大きいほうから順に(直感的に言うと,右側の成分から順に)コピーします.これらの違いが影響するのは,vec1vec2 が同じ場合です.2つのベクタが異なる場合には,どちらを使っても結果は同じです.

次の点に注意して下さい.

具体例 コピー元とコピー先が同じベクタの場合にコピーがどのように行われるかを試してみます. 以下では,ベクタ vec を適当に作って,vector-move-left! を使って,vec[2]〜vec[5] を vec[0]〜vec[3] にコピーしてみます.これは
vec[2] $\rightarrow$ vec[0], vec[3] $\rightarrow$ vec[1], vec[4] $\rightarrow$ vec[2], vec[5] $\rightarrow$ vec[3]
の順にコピーを行います.以下の実行例を見ると,vec[2]〜vec[5] の値である 20〜50 がそのまま vec[0]〜vec[3] にコピーされています.
guile> (define vec (vector 0 10 20 30 40 50 60 70))
guile> vec
$1 = #(0 10 20 30 40 50 60 70)
guile> (vector-move-left! vec 2 6 vec 0)
guile> vec
$2 = #(20 30 40 50 40 50 60 70)
guile> 

次に,ベクタ vec を作り直したあと,vector-move-left! を使って vec[2]〜vec[5] を vec[4]〜vec[7] にコピーしてみます.これは
vec[2] $\rightarrow$ vec[4], vec[3] $\rightarrow$ vec[5], vec[4] $\rightarrow$ vec[6], vec[5] $\rightarrow$ vec[7]
の順にコピーを行います.
guile> (define vec (vector 0 10 20 30 40 50 60 70))
guile> vec
$1 = #(0 10 20 30 40 50 60 70)
guile> (vector-move-left! vec 2 6 vec 4)
guile> vec
$2 = #(0 10 20 30 20 30 20 30)
この結果を見ると,vec[4] $\rightarrow$ vec[6],vec[5] $\rightarrow$ vec[7] のコピーを行うときの vec[4] と vec[5] はコピー後の値が使われていることが分かります.

補足 1つのベクタに対して vector-move-left! を使ってコピーする場合で, コピー元とコピー先の成分が重なっていて,コピー先がコピー元より右側にある場合, 重なっている成分に関してコピー後の値が再びコピーされます.一方, コピー先がコピー元より左側にある場合にはコピー後の値が再びコピーされることはありません.

このことは vector-move-right! についても同じが言えます.つまり, 1つのベクタに対して vector-move-right! を使ってコピーする場合で, コピー元とコピー先の成分が重なっていて,コピー先がコピー元より左側にある場合, 重なっている成分に関してコピー後の値が再びコピーされます.一方, コピー先がコピー元より右側にある場合にはコピー後の値が再びコピーされることはありません.

以上から,1つのベクタの中でコピー(転写)を行う場合, 左側にコピーするときには vector-move-left! を使い, 右側にコピーするときには vector-move-right! を使うのが一般的と言えます. 「left」と「right」はコピーする方向を示していると言ってもよいでしょう.

でも,vector-copy! を使えば「正しく」コピーできるので, これらの手続きを使うことはないように感じます.
(おしまい)