make-array, fill-pointer, vector

make-arrayのfill-pointerって何に使うの?死ぬの?と思ってたけど、vectorをスタックのように扱う際に力を発揮するんですね。
しかしmake-array関数が思ったより複雑なので日記にメモ化しましょうという。
まずは基本的な使い方

; tを渡すと:fill-pointerは3(vectorのサイズ)になる
; :fill-pointerを指定するとvector-push,vector-popなどの関数が使えるようになる
CL-USER> (make-array 3 :fill-pointer t)
#(NIL NIL NIL)

; 0〜3の間で指定することも可能。ポインタが0なのでスタックが見かけ上カラになる
; ただし消えるわけではなく見えないだけ(後述)
CL-USER> (setq s (make-array 3 :fill-pointer 0))
#()
CL-USER> (fill-pointer s)
0

; スタックポインタを1にすると0番目の要素だけ見える
CL-USER> (setq s (make-array 5 :fill-pointer 1 :initial-element 'x))
#(X)
CL-USER> (vector-push 'one s)
1
CL-USER> (vector-push 'two s)
2
; pushの戻り値は要素のnew-index
CL-USER> (elt s (vector-push 'three s))
THREE
CL-USER> s
#(X ONE TWO THREE)
; :fill-pointerはsetfで操作可能
CL-USER> (setf (fill-pointer s) 5)
5
; すると見えてなかっただけの最後の要素が出現
CL-USER> s
#(X ONE TWO THREE X)


入れすぎ出しすぎの場合

CL-USER> (setq s (make-array 3 :fill-pointer 0))
#()
CL-USER> (vector-push 1 s)
0
CL-USER> (vector-push 2 s)
1
CL-USER> (vector-push 3 s)
2
CL-USER> (vector-push 4 s)
NIL
CL-USER> s
#(1 2 3)

CL-USER> (vector-pop s)
3
CL-USER> (vector-pop s)
2
CL-USER> (vector-pop s)
1
CL-USER> (vector-pop s)
; Evaluation aborted
;; VECTOR-POP: #() has length zero

; ポインタがずれただけであって、popしても削除はされてない
CL-USER> (setf (fill-pointer s) 3)
3
CL-USER> s
#(1 2 3)

出しすぎはエラーになるのでfill-pointerやlengthを確認しておく必要がある。


make-arrayで:adjustableにtを与えるとスタックが可変サイズになる。

CL-USER> (setq s (make-array 3
			     :fill-pointer t
			     :adjustable t)) ;←ここ
#(NIL NIL NIL)
CL-USER> (vector-push 1 s)
NIL ; 通常のpushでは押し込めない
CL-USER> s
#(NIL NIL NIL)

; vector-push-extend を利用すると・・・
CL-USER> (vector-push-extend 2 s)
3
CL-USER> s
#(NIL NIL NIL 2)


element-typeをcharacterにすると(デフォルトはt)文字列が対象になる

CL-USER> (setq s (make-array 3
			     :element-type 'character
			     :fill-pointer t
			     :initial-element #\z))
"zzz"
CL-USER> (vector-pop s)
#\z
CL-USER> (vector-push #\y s)
2
CL-USER> s
"zzy"
CL-USER> (array-element-type s)
CHARACTER


特に意味はない

CL-USER> (setq s (make-array 12
			     :fill-pointer 4
			     :initial-contents "herro, world"
			     :element-type 'character))
"herr"
CL-USER> (vector-pop s)
#\r
CL-USER> (vector-pop s)
#\r
CL-USER> s
"he"

CL-USER> (vector-push #\l s)
2
CL-USER> (vector-push #\l s)
3
CL-USER> s
"hell"

CL-USER> (setf (fill-pointer s) 12)
12
CL-USER> s
"hello, world"

:displaced-to

make-arrayの:displaced-toを初めて知った。

CL-USER> (setq x (vector 1 2 3 4 5 6))
#(1 2 3 4 5 6)
; 一次元を二次元に
CL-USER> (make-array '(2 3) :displaced-to x)
#2A((1 2 3) (4 5 6))

; 括弧まみれの刑
CL-USER> (make-array '(2 3 1 1 1) :displaced-to x)
#5A(((((1))) (((2))) (((3)))) ((((4))) (((5))) (((6)))))


; 三次元を一次元に
CL-USER> (setq x (make-array '(2 3 2) :initial-element 0))
#3A(((0 0) (0 0) (0 0)) ((0 0) (0 0) (0 0)))
CL-USER> (make-array 12 :displaced-to x)
#(0 0 0 0 0 0 0 0 0 0 ...)
; 減る分には問題ないみたい
CL-USER> (make-array 3 :displaced-to x)
#(0 0 0)