vector-append を実装してみた

R5RS では

(length list)
(string-length string)
(vector-length vector)

のようにデータ構造によって手続きが分かれている。

これが気に入らない。

Haskell の length や Python の len のように「長さを求める」という抽象化された手続きがひとつにまとまっていたほうがコードがすっきりする(ということで作ったのが Scheme で length 手続きを再実装 - satosystemsの日記)。

(append list ...)
(string-append string ...)

append もデータ構造で手続きが分かれている。そして vector 用の手続きがないのもムズムズする。

Schemevector は、C++Java などのそれと違い可変長ではない(可変長は list)。どちらかというと配列のイメージが近い。なので append がないのだと思う。

でもあえて、vector-append を作ってみることにする。

(define vector-append
  (lambda (a . b)
    (list->vector (append (vector->list a) b))))

たぶんこれがもっとも正解に近いはず。

(define vector-append2
  (lambda (a . b)
    (let ((v (make-vector (+ (vector-length a) (length b)))))
      (let loop ((i 0))
        (if (< i (vector-length a))
          ((vector-set! v i (vector-ref a i))
            (loop (+ i 1)))
          ((if (< i (+ (vector-length a) (length b)))
              ((vector-set! v i (list-ref b (- i (vector-length a))))
                (loop (+ i 1)))
              v)))))))

list->vectorvector->list を使わないバージョンも作ってみたけど、期待通りに動作しない。そしてその原因がよくわからない。

作ってみてわかったんだけど、vector-append はたとえば

(define v #(1 2 3))

(print (vector-append v 4 5))
(print v)

という風にしてみれば一目瞭然で、元の v に要素が追加できていない。C の realloc みたいな使い方をしたいんだけど Scheme ではそういうことができないので、vector-append は R5RS には含まれないのだ、と解釈した。


なお、後で気がついたんだけど SRFI 43: Vector LibrarySRFI-43 として vector-append が提案されていた。僕の考える仕様とはちょっと異なるんだけど、こちらの仕様もやっぱり新たに vector を割り当てるようになっているので、やっぱり vector の動的拡張は無理みたい。