モジュールのロード
このノートで説明する手続きを利用するためには,以下に示すように (srfi srfi-43) モジュールをロードする必要があります.
(use-modules (srfi srfi-43))
ベクタの作成
vector-unfold ― ベクタの展開生成(left-to-right)
procedure:
(vector-unfold proc len seed ... )
proc |
手続き |
len |
ベクタの長さ(成分数)を表す整数値 |
seed |
成分を生成するための種 |
返り値 |
ベクタ |
この手続きは,手続き
proc と
seed ... を使って長さが
len のベクタを作成して返します.
この手続きの呼び出しは次のいずれかの形式をしています.
(a) (vector-unfold $f$ $\ell$)
(b) (vector-unfold $f$ $\ell$ $s_1$ ... $s_n$)
ここで,$f$ は
proc を, $\ell$ は
len を,$s_1$,...,$s_n$ は
seed ... をそれぞれ表しています.
(a) の場合,$f$ はベクタの添字 $i$ を引数とする1引数の手続きでなければいけません.(a) の呼び出しは $f(0)$,$f(1)$,..., $f(\ell-1)$ を成分とするベクタを返します.
(b) の場合,$f$ は $n+1$ 個の値を引数として受け取り,$n+1$ 個の値を返す手続きでなければいけません.
その第1引数にはベクタの添字が渡され,残りの引数にはベクタの成分を生成するための種となる値が渡されます.$n+1$ 個の返り値のうち,その第1の値はベクタの成分として使われ,残りの $n$ 個の値は次の成分を生成するための種として使われます.
(b) の呼び出しは,次のように定義される $v_0$,...,$v_{\ell-1}$ を成分とするベクタを返します.
\(
\begin{array}{lcl}
(v_0,s_{1}^{(1)},...,s_{n}^{(1)}) & = & f(0,s_1,...,s_n) \\
(v_1,s_{1}^{(2)},...,s_{n}^{(2)}) & = & f(1,s_{1}^{(1)},...,s_{n}^{(1)}) \\
(v_2,s_{1}^{(3)},...,s_{n}^{(3)}) & = & f(2,s_{1}^{(2)},...,s_{n}^{(2)}) \\
&...& \\
(v_i,s_{1}^{(i+1)},...,s_{n}^{(i+1)}) & = & f(i,s_{1}^{(i)},...,s_{n}^{(i)}) \\
&...& \\
(v_{\ell-1},s_{1}^{(\ell)},...,s_{n}^{(\ell)}) & = & f(\ell-1,s_{1}^{(\ell-1)},...,s_{n}^{(\ell-1)}) \\
\end{array}
\)
具体例
以下では,添字 $i$ の2乗 $i^2$ を成分とする長さ5のベクタを生成しています.
guile> (use-modules (srfi srfi-43))
guile> (vector-unfold (lambda (i) (* i i)) 5)
$1 = #(0 1 4 9 16)
具体例
以下の実行例では,
フィボナッチ数を成分とするベクタを生成しています.
Wikpedia[フィボナッチ数]によれば,フィボナッチ数 $F_i$ は次の漸化式で定義されます.
$F_0 = 0$, $F_1 = 1$, $F_{n+2} = F_{n+1} + F_n$ $(n \geq 0)$
以下の fib は
$fib(i,F_i,F_{i+1}) = (F_i,F_{i+1},F_{i+2})$ $(i \geq 0)$
という条件を満たす多値関数 $fib$ を計算しています.
下記の vector-unfold 手続きに渡されている第3引数の 0 は $F_0$ のことであり,
第4引数の 1 は $F_1$ のことです.
guile> (define (fib i a b) (values a b (+ a b)))
guile> (vector-unfold fib 10 0 1)
$2 = #(0 1 1 2 3 5 8 13 21 34)
具体例
以下の suffix-vector 手続きは,文字列 str の接尾辞(第$i$文字目から末尾までの部分列)からなるベクタを返します.
;; srfi-43.scm
(use-modules (srfi srfi-43))
(define (suffix-vector str)
(vector-unfold (lambda (i s) (values s (substring s 1)))
(string-length str)
str))
$ guile -l srfi-43.scm
...... コンパイルメッセージや起動メッセージ ......
guile> (suffix-vector "abcdef")
$1 = #("abcdef" "bcdef" "cdef" "def" "ef" "f")
vector-unfold-right ― ベクタの展開生成(right-to-left)
procedure:
(vector-unfold-right proc len seed ... )
proc |
手続き |
len |
ベクタの長さ(成分数)を表す整数値 |
seed |
成分を生成するための種 |
返り値 |
ベクタ |
この手続きは vector-unfold と同じように動作しますが,最後の成分から先頭の成分に向かってベクタを構成します.この手続きの呼び出しは次のいずれかの形式をしています.
(a) (vector-unfold-right $f$ $\ell$)
(b) (vector-unfold-right $f$ $\ell$ $s_1$ ... $s_n$)
ここで,$f$ は
proc を, $\ell$ は
len を,$s_1$,...,$s_n$ は
seed ... をそれぞれ表しています.
(a) の場合,$f$ はベクタの添字 $i$ を引数とする1引数の手続きでなければいけません.(a) の呼び出しは $f(\ell-1)$,$f(\ell-2)$,..., $f(0)$ の順にベクタの成分を構成します.ただし,ベクタの成分は添字$i$の通りになります.つまり,
(vector $f(0)$ $f(1)$ ... $f(\ell-1)$)
というベクタを返します.$f$の計算が副作用を伴わないときには vector-unfold と同じ結果になります.
(b) の場合,$f$ は $n+1$ 個の値を引数として受け取り,$n+1$ 個の値を返す手続きでなければいけません.
その第1引数にはベクタの添字が渡され,残りの引数にはベクタの成分を生成するための種となる値が渡されます.$n+1$ 個の返り値のうち,その第1の値はベクタの成分として使われ,残りの $n$ 個の値は次の成分を生成するための種として使われます.
(b) の呼び出しは,次のように定義される $v_{\ell-1}$,...,$v_0$ をこの順に計算して,
(vector $v_0$ $v_1$ ... $v_{\ell-1}$)
というベクタを返します.
\(
\begin{array}{lcl}
(v_{\ell-1},s_{1}^{(1)},...,s_{n}^{(1)}) & = & f(\ell-1,s_1,...,s_n) \\
(v_{\ell-2},s_{1}^{(2)},...,s_{n}^{(2)}) & = & f(\ell-2,s_1^{(1)},...,s_n^{(1)}) \\
&...& \\
(v_{\ell-i},s_{1}^{(i)},...,s_{n}^{(i)}) & = & f(\ell-i,s_{1}^{(i-1)},...,s_{n}^{(i-1)}) \\
&...& \\
(v_0,s_{1}^{(\ell)},...,s_{n}^{(\ell)}) & = & f(0,s_{1}^{(\ell-1)},...,s_{n}^{(\ell-1)}) \\
\end{array}
\)
具体例
vector-unfold と vector-unfold-right の評価順序の違いを確認しておきましょう.
guile> (use-modules (srfi srfi-43))
guile> (vector-unfold (lambda (i) (display i) (newline) i) 5)
0
1
2
3
4
$1 = #(0 1 2 3 4)
guile> (vector-unfold-right (lambda (i) (display i) (newline) i) 5)
4
3
2
1
0
$2 = #(0 1 2 3 4)
(a)の場合,両者は各成分の評価順序が異なるだけで,
procが副作用を伴わない限り,返り値のベクタは同じになります.そこで,
procが副作用を伴うような具体例を示します.以下では,変数 x を定義する前に proc を定義しているのでwarningメッセージが出ていますが,そのあとの計算には影響ありません.
guile> (define (proc i) (let ((temp x)) (set! x (1+ x)) temp))
;;; <stdin>:2:17: warning: possibly unbound variable `x'
guile> (define x 10)
guile> (vector-unfold proc 5)
$3 = #(10 11 12 13 14)
guile> (define x 10)
guile> (vector-unfold-right proc 5)
$4 = #(14 13 12 11 10)
具体例
vector-unfold について示した具体例を vector-unfold-right に変更すると成分が逆順に並んだベクタが生成されます.以下はフィボナッチ数を成分とするベクタを生成しています.
guile> (use-modules (srfi srfi-43))
guile> (define (fib i a b) (values a b (+ a b)))
guile> (vector-unfold-right fib 10 0 1)
$1 = #(34 21 13 8 5 3 2 1 1 0)
以下のプログラムは,接尾辞からなるベクタを生成するプログラムを vector-unfold-right に変更しています.
;; srfi-43.scm
(use-modules (srfi srfi-43))
(define (suffix-vector str)
(vector-unfold-right (lambda (i s) (values s (substring s 1)))
(string-length str)
str))
$ guile -l srfi-43.scm
...... コンパイルメッセージや起動メッセージ ......
guile> (suffix-vector "abcdef")
$1 = #("f" "ef" "def" "cdef" "bcdef" "abcdef")
vector-copy ― ベクタのコピー
procedure:
(vector-copy vec)
(vector-copy vec start)
(vector-copy vec start end)
(vector-copy vec start end obj)
vec |
ベクタ |
start |
コピーを開始する位置.省略時は0に設定されます. |
end |
コピーを終了する位置.省略時はベクタ(vec)の長さに設定されます. |
obj |
オブジェクト.省略時は unspecified になります. |
返り値 |
ベクタ |
注意
ベクタ(
vec)の
start 番目から
end$\,-1$ 番目までの成分からなるベクタを新たに作成して返します.
start を省略したときには 0 に設定され,
end を省略したときにはベクタ(
vec)の長さ(成分数)に設定されます.
次の点に注意して下さい.
具体例
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 の長さのベクタが生成され,コピー元から必要なコピーを行った残りの成分は,
obj を指定した場合には
obj が設定され,省略した場合には unspecified になります.
guile> (vector-copy vec 3 10 'X)
$3 = #(30 40 50 X X X X)
guile> (vector-copy vec 3 10)
$4 = #(30 40 50 #<unspecified> #<unspecified> #<unspecified> #<unspecified>)
start $=$
end だったり,
start がベクタの長さだった場合,空ベクタが作成されます.
guile> (vector-copy vec 2 2)
$5 = #()
guile> (vector-copy vec 6 6)
$6 = #()
guile> (vector-copy vec 6)
$7 = #()
vector-reverse-copy ― ベクタの逆順コピー
procedure:
(vector-reverse-copy vec)
(vector-copy vec start)
(vector-copy vec start end)
vec |
ベクタ |
start |
コピーを開始する位置.省略時は0に設定されます. |
end |
コピーを終了する位置.省略時はベクタ(vec)の長さに設定されます. |
返り値 |
ベクタ |
この手続きは,
ベクタ(
vec)の
start 番目から
end$\,-1$ 番目までの成分を逆順に並べたベクタを新たに作成して返します.
start を省略したときには 0 に設定されます.
end を省略したときにはベクタ(
vec)の長さ(成分数)に設定されます.
次の点に注意して下さい.
具体例
guile> (use-modules (srfi srfi-43))
guile> (define vec #(0 10 20 30 40 50 60))
guile> vec
$1 = #(0 10 20 30 40 50 60)
guile> (vector-reverse-copy vec)
$2 = #(60 50 40 30 20 10 0)
guile> (vector-reverse-copy vec 2)
$3 = #(60 50 40 30 20)
guile> (vector-reverse-copy vec 2 5)
$4 = #(40 30 20)
guile> (vector-reverse-copy vec 2 2)
$5 = #()
guile> (vector-reverse-copy vec 7)
$6 = #()
guile> (vector-reverse-copy vec 7 7)
$7 = #()
vector-append ― ベクタの連結
procedure:
(vector-append vec ... )
この手続きは,0個以上のベクタ
vec ... を連結したベクタを新たに作成して返します.
vec を1つも指定しなかったときには空ベクタを返します.
具体例
guile> (use-modules (srfi srfi-43))
guile> (vector-append #(10 20 30) #(40 50 60) #(70 80))
$1 = #(10 20 30 40 50 60 70 80)
guile> (vector-append)
$2 = #()
vector-concatenate ― ベクタの連結
procedure:
(vector-concatenate list-of-vecs ... )
list-of-vecs |
ベクタのリスト |
返り値 |
ベクタ |
この手続きは,
list-of-vecs に属するベクタを連結したベクタを新たに作成して返します.
list-of-vecs が空リストのときには空ベクタを返します.これは,
(apply vector-append list-of-vecs)
と等価です.
具体例
guile> (use-modules (srfi srfi-43))
guile> (vector-concatenate '( #(10 20 30) #(40 50 60) #(70 80) ))
$1 = #(10 20 30 40 50 60 70 80)
guile> (vector-concatenate '())
$2 = #()
ベクタの検査
vector-empty? ― 空ベクタの検査
procedure:
(vector-empty? vec)
この手続きは,
vec が空ベクタのときには #t を返し,空でないときには #f を返します.
vec はベクタでなければいけません.
具体例
guile> (use-modules (srfi srfi-43))
guile> (vector-empty? #(10 20 30))
$1 = #f
guile> (vector-empty? #())
$2 = #t
vector= ― ベクタの同等性判定
procedure:
(vector= pred? vec ...)
pred? |
述語 |
vec |
ベクタ |
返り値 |
ブール値 |
vector= の実引数として$n$個のベクタを指定したとき,
pred? は$n$個の引数を受け取る述語でなければいけません.このとき.vector= は,ベクタ
vec ... が同じ長さを持っていて,かつ,各成分が「等しい」とき #t を返し,そうでないとき #f を返します.ただし,各成分が「等しい」か否かは
pred? を使って判定します.ベクタを1つも指定しなかったときや1つしか指定しなかったときには, pred? を無視して #t を返します.
Guile[7.5.30.2 SRFI-43 Predicates]は,
pred? に関して
if (eq? a b) returns true, then (pred? a b) must also return true
という条件を満たさなければならないと述べています.
しかし,これは意味論的な整合性を堅持すべしというモラルを述べているだけで,
述語であれば何でも利用できます.
具体例
guile> (use-modules (srfi srfi-43))
guile> (vector= equal? #("a" "b" "c") #("a" "b" "c"))
$1 = #t
guile> (vector= equal? #("a" "b" "c") #("a" "X" "c"))
$2 = #f
pred? は述語であれば何でも利用できます.
以下の pred? は,
第1ベクタの成分がシンボルの Guile かどうかだけを検査していて,
第2ベクタを無視しています.この pred? は上で述べている条件を満たしていません.
例えば,(eq? 'Scheme 'Scheme) は真ですが,(pred? 'Scheme 'Scheme) は #f になります.でも,何の問題もなく機能します.
guile> (define (pred? x y) (eq? x 'Guile))
guile> (vector= pred? #(Guile Guile Guile) #("a" "X" "c"))
$3 = #t
guile> (vector= pred? #(Guile Scheme Guile) #("a" "X" "c"))
$4 = #f
なお,ベクタを1つしか指定しなかったときには pred? を無視して,常に #t を返します.
guile> (vector= pred? #(Scheme Scheme Scheme))
$5 = #t
それから,#f をけっして返すことのない手続きも利用できますが,
比較結果は常に真値になるので無意味です.
「述語」について
Schemeの仕様書は,「述語」をブール値を返す手続きのことと定義しています.
その一方で,#f 以外のすべての値を真値として解釈すると定めています.
そのため,上記の pred? は厳密な意味での「述語」でなくてもかまいません.
そればかりか,あらゆる手続きを指定できます.
従って,「述語でなければいけません」という記述はモラルを述べているだけのことです.
以後の説明に登場するすべての「述語」についても同様です.
ベクタに対する繰り返し
vector-fold ― ベクタの畳み込み(left fold)
procedure:
(vector-fold kons knil vec1 vec2 ... )
kons |
畳み込みを行う手続き |
knil |
累積値(下記参照)の初期値. |
vec$k$ |
ベクタ |
返り値 |
畳み込んだ値 |
この手続きの呼び出しは,一般に,次の形式をしています.
(vector-fold $f$ $a$ $vec_1$ ... $vec_n$)
ここで,$f$ は
kons を,$a$ は
knil を,$vec_1$ ... $vec_n$ は
vec1 vec2 ... を表しています.
このとき,$f$ は$n+2$個の引数を受け取り1つの値を返す手続きでなければいけません.
その第1引数はベクタの添字$i$が渡され,
第2引数は累積値(下記の$s$)が渡され,
残りの$n$個の引数は各ベクタの第$i$成分 $vec_1[i]$ ... $vec_n[i]$ が渡されます.$f$の返り値は計算途中の累積値(下記の$s$)または vector-fold の返り値として使われます.
上記の vector-fold の呼び出しは次のような計算を行います.
以下の $\ell_{min}$ は,もっとも短いベクタの長さを表します.
- $s \leftarrow a$
- $i=0,1, ..., \ell_{min}-1$ に対して(この順に)以下の計算を繰り返します.
- $s \leftarrow f(i,s,vec_1[i],..,vec_n[i])$
- 最後に求まった $s$ を返します.
これは,ベクタの左(先頭)の成分から順に$f$の適用結果を累積していって最終的な累積値を返しています.関数型プログラミングでは,このような計算構造のことを(左からの)
畳み込み(left fold とか left folding)と呼んでいるようです.
ベクタの長さは不揃いでもかまいません.不揃いの場合,上の計算が示しているように,もっとも短いベクタに合わせて畳み込みが行われます.
具体例
以下の実行例は数値ベクタ vec の総和を求めています.下記のラムダ式は,$i-1$ 番目までの成分の総和 $s$ と $i$ 番目の成分 $v$ から,$i$ 番目までの成分の総和 $s+v$ を求めています.
このlambda式の計算を先頭の成分から繰り返すことによってベクタ全体の総和を求めています.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(1 2 3 4 5))
guile> (vector-fold (lambda (i s v) (+ s v)) 0 vec)
$1 = 15
以下の実行例は数値ベクタ vec の最大値を求めています.ただし,ベクタの各成分が非負の数値であることを仮定しています.以下のlambda式は,$i-1$ 番目の成分までの最大値 $s$ と$i$ 番目の成分 $v$ から,$i$ 番目の成分までの最大値を求めています.
このlambda式の計算を先頭の成分から繰り返すことによってベクタ全体の最大値を求めています.
guile> (define vec #(4 2 5 1 3))
guile> (vector-fold (lambda (i s v) (if (> s v) s v)) 0 vec)
$2 = 5
ちなみに,以下の実行例のように,最大値候補の初期値をベクタの第0成分にすれば,
ベクタに対して何も仮定する必要はなくなります.
guile> (define vec #(-4 -2 -5 -1 -3))
guile> (vector-fold (lambda (i s v) (if (> s v) s v)) (vector-ref vec 0) vec)
$4 = -1
具体例
以下の実行例は2つの数値ベクタの内積(各成分の積の総和)を求めています.
guile> (use-modules (srfi srfi-43))
guile> (define vec1 #(1 2 3 4 5))
guile> (define vec2 #(5 4 3 2 1))
guile> (vector-fold (lambda (i s v1 v2) (+ s (* v1 v2))) 0 vec1 vec2)
$1 = 35
具体例
最終的な結果は,単純な値に限らず,リストにすることもできます.
例えば,以下の実行例は,シンボルからなるベクタに対して,'Guile という成分の添字のリストを求めています.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(Scheme Guile Common Lisp Clojure Guile))
guile> (vector-fold (lambda (i s v) (if (eq? v 'Guile) (cons i s) s)) '() vec)
$1 = (5 1)
vector-fold-right ― ベクタの畳み込み(right fold)
procedure:
(vector-fold-right kons knil vec1 vec2 ... )
kons |
畳み込みを行う手続き |
knil |
累積値(下記参照)の初期値. |
vec$k$ |
ベクタ |
返り値 |
畳み込んだ値 |
vector-fold-right は,ベクタの右(最後)の成分から順に累積値を求める点を除けば,vector-fold と類似の処理を行います.以下の説明は,vector-fold の説明をコピー&ペーストして少し変更したものです.下記の 2. のところでベクタの右(最後)の成分から順に計算していることに注意して下さい.
この手続きの呼び出しは,一般に,次の形式をしています.
(vector-fold-right $f$ $a$ $vec_1$ ... $vec_n$)
ここで,$f$ は
kons を,$a$ は
knil を,$vec_1$ ... $vec_n$ は
vec1 vec2 ... を表しています.
このとき,$f$ は$n+2$個の引数を受け取り1つの値を返す手続きでなければいけません.
その第1引数はベクタの添字$i$が渡され,
第2引数は累積値(下記の$s$)が渡され,
残りの$n$個の引数は各ベクタの第$i$成分 $vec_1[i]$ ... $vec_n[i]$ が渡されます.$f$の返り値は計算途中の累積値(下記の$s$)または vector-fold-right の返り値として使われます.
上記の vector-fold-right の呼び出しは次のような計算を行います.
以下の $\ell_{min}$ は,もっとも短いベクタの長さを表します.
- $s \leftarrow a$
- $i=\ell_{min}-1,\ell_{min}-1,...,0$ に対して(この順に)以下の計算を繰り返します.
- $s \leftarrow f(i,s,vec_1[i],..,vec_n[i])$
- 最後に求まった $s$ を返します.
これは,ベクタの右(最後)の成分から順に$f$の適用結果を累積していって最終的な累積値を返しています.関数型プログラミングでは,このような計算構造のことを(右からの)
畳み込み(right fold とか right folding)と呼んでいるようです.
ベクタの長さは不揃いでもかまいません.不揃いの場合,上の計算が示しているように,もっとも短いベクタに合わせて畳み込みが行われます.
具体例
vector-fold を使ってベクタからリストを作ると,リストの要素の並びがベクタの成分の並びとは逆順になります.一方,vector-fold-right を使うと同じ順に並びます.
以下は,vector-fold の項で示した最後の実行例を vector-fold-right を使って実行し直しています.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(Scheme Guile Common Lisp Clojure Guile))
guile> (vector-fold-right (lambda (i s v) (if (eq? v 'Guile) (cons i s) s)) '() vec)
$1 = (1 5)
vector-map ― ベクタの変換
procedure:
(vector-map proc vec1 vec2 ... )
proc |
変換を行う手続き |
vec$k$ |
ベクタ |
返り値 |
変換後のベクタ |
この手続きの呼び出しは,一般に,次の形式をしています.
(vector-map $f$ $vec_1$ ... $vec_n$)
ここで,$f$ は
proc を表し,$vec_1$,...,$vec_n$ は
vec1 vec2 ... を表しています.このとき,$f$ は$n+1$個の引数を受け取って1個の値を返す手続きでなければいけません.その第1引数はベクタの添字が渡され,残りの引数には各ベクタの第$i$成分が渡されます.返り値は新たなベクタの第$i$成分として使われます.
上記の手続き呼び出しは,次のように定義される $v_0$,...,$v_{\ell_{min}-1}$ を成分とする新たなベクタを作成して返します.ここで,$\ell_{min}$ はもっとも短いベクタの長さを表しています.
$v_i = f(i,vec_1[i],...,vec_n[i])$ ($0 \leq i \leq \ell_{min}-1$)
ただし,$v_0$,...,$v_{\ell_{min}-1}$ を求める順番は不定(unspecified)です.従って,
procの中で評価順序を期待するような副作用を伴った操作(例えば,添字の小さい順に成分を表示するなど)を行うべきではないでしょう.
そのような副作用を目的とする操作は,
一般に,後述する vector-for-each を使って行います.
ベクタの長さは不揃いでもかまいません.
不揃いの場合,上の計算が示しているように,
もっとも短いベクタに合わせて変換が行われます.
具体例
以下は,ベクタの各成分を2乗したベクタを生成しています.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(1 2 3 4 5))
guile> (vector-map (lambda (i v) (* v v)) vec)
\DOL1 = #(1 4 9 16 25)
以下は,2つのベクタに対して,各成分の小さい方を成分とするベクタを生成しています.
guile> (define vec1 #(13 25 31 48 54))
guile> (define vec2 #(14 22 39 43 56))
guile> (vector-map (lambda (i v1 v2) (if (< v1 v2) v1 v2)) vec1 vec2)
$2 = #(13 22 31 43 54)
vector-map! ― ベクタの改変
procedure:
(vector-map! proc vec1 vec2 ... )
proc |
変換を行う手続き |
vec$k$ |
ベクタ |
返り値 |
unspecified |
vector-map のところで定義した $v_0$,...,$v_{\ell_{min}-1}$ を求めて
vec1 に保存します.
vec1 が $\ell_{min}$ よりも長い場合,残りの成分は変更されません.
proc は vector-map の場合と同様の手続きでなければいけません.さらに,vec1 は変更可能(mutable)でなければいけません.また,各成分を求める順番は不定(unspecified)です.
具体例
以下は,2つのベクタの各成分ごとの積を求めて第1ベクタに保存しています.
第1ベクタは変更可能(mutable)でなければいけないので,vector 手続きを使って定義しています.一方,第2ベクタはどちらでもよいのでベクタ定数として定義しています.
それから,第1ベクタを第2ベクタより長くしていて,
第1ベクタの残りの成分が変化しないことを確認しています.
guile> (use-modules (srfi srfi-43))
guile> (define vec1 (vector 2 2 2 2 2 2 2 2))
guile> vec1
$1 = #(2 2 2 2 2 2 2 2)
guile> (define vec2 #(1 2 3 4 5))
guile> vec2
$2 = #(1 2 3 4 5)
guile> (vector-map! (lambda (i v1 v2) (* v1 v2)) vec1 vec2)
guile> vec1
$3 = #(2 4 6 8 10 2 2 2)
vector-for-each ― ベクタに関する繰り返し
procedure:
(vector-for-each proc vec1 vec2 ... )
proc |
繰り返しの本体を実行する手続き |
vec$k$ |
ベクタ |
返り値 |
unspecified |
この手続きの呼び出しは,一般に,次の形式をしています.
(vector-for-each $f$ $vec_1$ ... $vec_n$)
ここで,$f$ は
proc を表し,$vec_1$,...,$vec_n$ は
vec1 vec2 ... を表しています.このとき,$f$ は$n+1$個の引数を受け取る手続きでなければいけません.第1の引数はベクタの添字が渡され,残りの引数には各ベクタの第$i$成分が渡されます.$f$ は何らかの値を返してもかまいませんが棄却されます.
上記の手続き呼び出しは,
各成分に対して左(先頭)から右(最後)に向かって $f$ を適用します.つまり,
$f(0,vec_1[0],...,vec_n[0])$
$f(1,vec_1[1],...,vec_n[1])$
...
$f(\ell_{min}-1,vec_1[\ell_{min}-1],...,vec_n[\ell_{min}-1])$
といった順に $f$ を実行します.
ここで,$\ell_{min}$ はもっとも短いベクタの長さを表しています.
各成分に対する評価順序を前提とする副作用を伴う処理は,一般に,vector-for-each を使って処理します.
具体例
以下は,ベクタの各成分を添字の小さい順に表示しています.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(1 2 3 4 5))
guile> (vector-for-each (lambda (i v) (display v) (newline)) vec)
1
2
3
4
5
vector-count ― 成分のカウント
procedure:
(vector-count pred? vec1 vec2 ... )
pred? |
述語 |
vec$k$ |
ベクタ |
返り値 |
pred? が真となる成分の個数 |
実引数として指定したベクタの個数を$n$とおくとき,
pred? は$n+1$個の引数を受け取る述語でなければいけません.その第1引数はベクタの添字$i$が渡され,残りの$n$個の引数には各ベクタの第$i$成分が渡されます.
vector-count は
pred? が真となる添字の個数を返します.
具体例
以下は,2つのベクタに対して,(eqv?の意味で)同じ値の成分の個数を求めています.
guile> (use-modules (srfi srfi-43))
guile> (define vec1 #(Scheme Guile Common Lisp Clojure Guile Scheme))
guile> (define vec2 #(Scheme Guile Guile Scheme Scheme Guile Scheme))
guile> (vector-count (lambda (i v1 v2) (eqv? v1 v2)) vec1 vec2)
$1 = 4
ベクタの探索
vector-index{-right},vector-skip{-right} ― 添字を探索
procedure:
(vector-index pred? vec1 vec2 ... )
(vector-index-right pred? vec1 vec2 ... )
(vector-skip pred? vec1 vec2 ... )
(vector-skip-right pred? vec1 vec2 ... )
pred? |
述語 |
vec$k$ |
ベクタ |
返り値 |
添字 |
実引数として指定したベクタの個数を $n$ とおくとき,
pred? は $n$ 個の引数を受け取る述語でなければいけません.$n$個の引数は各ベクタの成分が渡されます.
もっとも短いベクタの長さを $\ell_{min}$,
pred? を$p$,
実引数として指定してベクタを $vec_1$,...,$vec_n$ とおくとき,vector-index は,$i=0,1,...,\ell_{min}-1$ の順に
$p(vec_1[i],...,vec_n[i])$
を求めていって,初めて真となる添字(つまり,真となる最小の添字)を返します.vector-index-right は,逆に,添字の大きい順($i=\ell_{min}-1,...,1,0$の順)に求めていって,初めて真となる添字(つまり,真となる最大の添字)を返します.真となる添字が見つからないときには,両方とも #f を返します.
vector-skip と vector-skip-right は,それぞれ,
pred? が偽となる最小の添字と最大の添字を返します.そういった添字がないときには #f を返します.
上記のどの手続きについても,ベクタの長さは不揃いでもかまいません.
具体例
以下では,2つの数値ベクタ vec1 と vec2 に対して,vec1$[i]$ $\lt$ vec2$[i]$ を満たす最小の添字(以下の2)と最大の添字(以下の3)を求めています.
guile> (use-modules (srfi srfi-43))
guile> (define vec1 #(3 5 4 1 5 9 2))
guile> (define vec2 #(2 1 7 8 2))
guile> vec1
$1 = #(3 5 4 1 5 9 2)
guile> vec2
$2 = #(2 1 7 8 2)
guile> (vector-index < vec1 vec2)
$3 = 2
guile> (vector-index-right < vec1 vec2)
$4 = 3
以下では,上記のベクタ vec1 に対して,奇数ではない成分の最小の添字と最大の添字を求めています.
guile> (vector-skip odd? vec1)
$5 = 2
guile> (vector-skip-right odd? vec1)
$6 = 6
vector-any,vector-every ― ベクタ版の or 形式と and 形式
procedure:
(vector-any pred? vec1 vec2 ... )
(vector-every pred? vec1 vec2 ... )
pred? |
述語 |
vec$k$ |
ベクタ |
返り値 |
成分の値 |
実引数として指定したベクタの個数を $n$ とおくとき,
pred? は $n$ 個の引数を受け取る述語でなければいけません.$n$個の引数は各ベクタの成分が渡されます.
もっとも短いベクタの長さを $\ell_{min}$,
pred? を$p$,
実引数として指定してベクタを $vec_1$,...,$vec_n$ とおくとき,vector-any は,
$p(vec_1[i],...,vec_n[i])$が真となる最小の添字 $i$($0 \leq i \lt \ell_{min}$)を求めて,$p(vec_1[i],...,vec_n[i])$ の値を返します.真となる添字が1つもないときには #f を返します.直感的に言うと,ベクタ版の or 形式と言えます.
一方,vector-every は,すべての添字 $i$($0 \leq i \lt \ell_{min}$)に対して $p(vec_1[i],...,vec_n[i])$ が真だったとき,$p(vec_1[\ell_{min}-1],...,vec_n[\ell_{min}-1])$ の値を返します.ある添字で偽になったときには #f を返します.直感的に言うと,ベクタ版の and 形式と言えます.
vector-binary-search ― ベクタの二分探索
procedure:
(vector-binary-search vec val cmp)
(vector-binary-search vec val cmp start)
(vector-binary-search vec val cmp start end)
vec |
ベクタ |
val |
適当な値 |
cmp |
手続き |
start |
ベクタの添字.省略時は0に設定されます. |
end |
ベクタの添字.省略時はベクタの長さに設定されます. |
返り値 |
ベクタの添字 |
cmp は2つの引数を受け取って,
数値を返す手続きでなければいけません.
ただし,その返り値に対して zero? が適用されるので,
一般的には正確数を返す手続きにしたほうがよいでしょう.
vec,
val,
cmp をそれぞれ,
$vec$,$val$,$cmp$ とおき,次の条件(*)が成り立っていると仮定します.
(*) |
添字 $p_1,p_2$(start $ \leq p_1 \leq p_2 \lt$ end)
が存在して,
- start $\leq i \lt p_1$ を満たす添字 $i$ に対して $cmp(vec[i],val) \lt 0$,かつ
- $p_1 \leq i \leq p_2$ を満たす添字 $i$ に対して $cmp(vec[i],val)=0$,かつ
- $p_2 \lt i \lt$ end を満たす添字 $i$ に対して $cmp(vec[i],val) \gt 0$
|
このとき,vector-binary-search 手続きは,二分探索法を使って,
上の条件(*)のb.を満たす添字 $i$($p_1 \leq i \leq p_2$) を返します.
ただし,(srfi srfi-43) モジュールが実装している二分探索アルゴリズムは,
条件(*)のb.を満たす添字 $i$ が見つかったら直ちにそれを返すようにしていて,
条件(*)に示した$p_1$や$p_2$を求めようとはしていません.そのため.
$p_1$〜$p_2$のどの添字を返すかは探索範囲(
startと
end)に依存していて,一般的なことは何も言えません.
条件(*)は二分探索法が合理的に動作し,かつ,
目的とする添字が発見できる条件を示しています.
二分探索法が合理的に動作する条件は次のようになります.
(+) |
添字 $p$(start $ \leq p \leq$ end)
が存在して,
- start $\leq i \lt p$ を満たす添字 $i$ に対して $cmp(vec[i],val) \leq 0$,かつ
- $p \leq i \lt$ end を満たす添字 $i$ に対して $cmp(vec[i],val) \geq 0$
|
vector-binary-search は,
この条件を満たし目的とする添字が見つからないときには #f を返します.
一方,この条件を満たさないとき,何を返すかは一般に不明です.
運良く(*)のb.を満たす添字 $i$ を返すかも知れませんし,#f を返すかも知れません.
補足
vec,
val,
cmp は条件(*)を満たせば何でもかまいません.
それから,
val は(クロージャとして)
cmp 手続きに組み込めるので,引数として指定する必要はなかったと思います.例えば,以下の具体例では
val を使いません.
具体例
下記の generate-cmp は,2つの文字 c1,c2 を受け取って,
次のように動作する手続きを返します.
- 文字列 str が c1 を含み c2 を含まないときには -1 を返し,
c1 を含まず c2 を含むときには 1 を返し,
どちらでもないときには 0 を返す.
なお,val は vector-binary-search の仕様に合わせているだけのダミーの引数であって,使いません.
;; vector.scm
(define (generate-cmp c1 c2)
(lambda (str val)
(let ((i1 (string-index str c1))
(i2 (string-index str c2)))
(cond
((and i1 (not i2)) -1)
((and (not i1) i2) 1)
(else 0)))))
これによって生成される手続きを使って二分探索を行ってみることにします.
以下では,文字列を成分とするベクタ(vec)に対して,文字 a と z を含まない成分の添字を求めています.なお,vector-binary-search の
val は使用しないので空文字列 "" を指定しています.また,下記の vec と cmp (と "")は条件(*)を満たしています.
guile> (load "vector.scm")
guile> (use-modules (srfi srfi-43))
guile> (define cmp (generate-cmp #\a #\z))
guile> (define vec #("ab" "#a#" "binary" "search" "###" "vector" "+++" "xyz" "#z#"))
guile> (vector-binary-search vec "" cmp)
$1 = 4
ベクタの改変
vector-swap! ― 成分の交換
procedure:
(vector-swap! vec i j)
vec |
ベクタ |
i,j |
ベクタの添字 |
返り値 |
unspecified |
これは,
vec の第$i$成分と第$j$成分を交換します.
vec は変更可能(mutable)でなければいけません.
guile> (use-modules (srfi srfi-43))
guile> (define vec (vector 0 1 2 3 4 5))
guile> vec
$1 = #(0 1 2 3 4 5)
guile> (vector-swap! vec 1 3)
guile> vec
$2 = #(0 3 2 1 4 5)
vector-reverse! ― ベクタの反転
procedure:
(vector-reverse! vec)
(vector-reverse! vec start)
(vector-reverse! vec start end)
vec |
ベクタ |
start
| ベクタの添字.省略時は0に設定されます. |
end
| ベクタの添字.省略時はベクタの長さに設定されます. |
返り値 |
unspecified |
これは,
vec の
start〜
end$\,-1$ の部分を反転します.
guile> (use-modules (srfi srfi-43))
guile> (define vec (vector 0 1 2 3 4 5 6 7 8 9))
guile> vec
$1 = #(0 1 2 3 4 5 6 7 8 9)
guile> (vector-reverse! vec 2 7)
guile> vec
$2 = #(0 1 6 5 4 3 2 7 8 9)
vector-reverse-copy! ― ベクタの反転
procedure:
(vector-reverse-copy! dst at src)
(vector-reverse-copy! dst at src start)
(vector-reverse-copy! dst at src start end)
dst |
ベクタ |
at |
ベクタ(dst)の添字.コピーを保存する先頭の位置. |
src |
ベクタ |
start |
ベクタ(src)の添字.コピ―を開始する位置.省略時は0に設定されます. |
end |
ベクタ(src)の添字.コピーを終了する位置.省略時はベクタ(src)の長さに設定されます. |
返り値 |
unspecified |
この手続きは,
src の
start〜
end$\,-1$ の部分を反転したものを
dst の
at 以降に保存します.
ただし,次の条件が成り立つときエラーが発生します.
- dst と src が同じベクタであって,
- コピー元の範囲とコピー先の範囲が重なっていて,
- at $\not=$ start が成り立つ.
at $=$
start のときには vector-reverse! と同じ振る舞いをします.
guile> (use-modules (srfi srfi-43))
guile> (define vec (vector 0 1 2 3 4 5 6 7 8 9))
guile> vec
$1 = #(0 1 2 3 4 5 6 7 8 9)
guile> (vector-reverse-copy! vec 2 vec 6 9)
guile> vec
$2 = #(0 1 8 7 6 5 6 7 8 9)
ベクタとリストの相互変換
vector->list,reverse-vector->list ― ベクタからリストへの変換
procedure:
(vector->list vec)
(vector->list vec start)
(vector->list vec start end)
(reverse-vector->list vec)
(reverse-vector->list vec start)
(reverse-vector->list vec start end)
vec |
ベクタ |
start |
ベクタの添字.省略時は0に設定されます. |
end |
ベクタの添字.省略時はベクタの長さに設定されます. |
返り値 |
リスト |
vector->list は,
vecの
start番目から
end$\,-1$番目の成分を要素とするリストを新たに作成して返します.reverse-vector->list は反転したリストを返します.
guile> (use-modules (srfi srfi-43))
guile> (define vec #(0 1 2 3 4 5 6 7 8 9))
guile> (vector->list vec 2 7)
$1 = (2 3 4 5 6)
guile> (reverse-vector->list vec 2 7)
$2 = (6 5 4 3 2)
list->vector,reverse-list->vector ― リストからベクタへの変換
procedure:
(list->vector lst lst)
(list->vector lst start)
(list->vector lst start end)
(reverse-list->vector lst)
(reverse-list->vector lst start)
(reverse-list->vector lst start end)
lst |
真正リスト |
start |
リストの添字.省略時は0に設定されます. |
end |
リストの添字.省略時はリストの長さに設定されます. |
返り値 |
ベクタ |
list->vector は,
lstの
start番目から
end$\,-1$番目の要素を成分とするベクタを新たに作成して返します.reverse-list->vector は反転したベクタを返します.
guile> (define lst '(0 1 2 3 4 5 6 7 8 9))
guile> (list->vector lst 2 7)
$3 = #(2 3 4 5 6)
guile> (reverse-list->vector lst 2 7)
$4 = #(6 5 4 3 2)