tamuraです。
どばーっと一気にメモリに読み込んで、それを1行ずつ切り出して字句解析したいとき、 読み込んだメモリを新しい領域を作成せずにどう切り出すのか、わからなかったので調べました。
Cでいうところのこんな感じのソース。
typedef struct LineBuffer_ {
char* buf;
int length;
} LineBuffer;
char* all_buffer;
LineBuffer* next_line(int pos) {
LineBuffer* buf = (LineBuffer *)malloc(sizeof (LineBuffer));
int len;
buf->buf = &(all_buffer[pos]);
for (len = 0; all_buffer[pos] != '\n' && all_buffer[pos] != '\0'; len++, pos++) {
}
buf->length = len;
return buf;
}
int main(int argc, char** argv) {
all_buffer = "this is a pen\nis this a pen?\n";
LineBuffer* b1 = next_line(0);
LineBuffer* b2 = next_line(b1->length + 1);
printf("%.*s:%d\n", b1->length, b1->buf, b1->length);
printf("%.*s:%d\n", b2->length, b2->buf, b2->length);
return 0;
}
これを実行すると
this is a pen:13
is this a pen?:14
こういう結果が得られます。
各LineBuffer
のbuf
は大元のall_buffer
を指しているだけです。
(なのでall_buffer
を変更するとLineBuffer
にあるbuf
も変わる。同じアドレスを指しているので)
こういうのをCommonLispでやるには、make-array
でオプションを指定するとのことでした。
make-array
http://clhs.lisp.se/Body/f_mk_ar.htm
これの displaced-to
と displaced-index-offset
を使いました。
CL-USER> (setf *a* #(1 2 3 4 5))
#(1 2 3 4 5)
;; *a*を元に*b*を作成
CL-USER> (setf *b* (make-array 2 :displaced-to *a* :displaced-index-offset 2))
#(3 4)
;; *a*の値を変更
CL-USER> (setf (aref *a* 2) 100)
100
CL-USER> *a*
#(1 2 100 4 5)
;; *b*の値も変わっている(同じアドレスを指しているっぽい)
CL-USER> *b*
#(100 4)
関数化
調子に乗って改行まで読み込む関数を作ってみました。
(defclass <line-buffer> ()
((buffer :initarg :buffer
:accessor buffer
:initform #())
(length :initarg :length
:accessor buf-length
:initform 0)))
(defvar *all-buffer* "this is a pen
is this a pen?
")
(defun next-line (pos)
(let ((line-buffer (make-instance '<line-buffer>))
(end (1- (length *all-buffer*))))
(loop for len = 0 then (1+ len)
for idx from pos to end
for c = (aref *all-buffer* idx)
when (char= c #\Newline)
do
(setf (buffer line-buffer) (make-array len :displaced-to *all-buffer*
:displaced-index-offset pos))
(setf (buf-length line-buffer) len)
(return line-buffer)
finally
(setf (buffer line-buffer) (make-array len :displaced-to *all-buffer*
:displaced-index-offset pos))
(setf (buf-length line-buffer) len)
(return line-buffer))))
実行すると
;; 最初の行
CL-USER> (setf buf1 (next-line 0))
#<<LINE-BUFFER> #x30200169369D>
CL-USER> (buffer buf1)
"this is a pen"
CL-USER> (buf-length buf1)
13
;; 次の行
CL-USER> (setf buf2 (next-line 14))
#<<LINE-BUFFER> #x3020016890BD>
CL-USER> (buffer buf2)
"is this a pen?"
CL-USER> (buf-length buf2)
14
C版と同じ結果が得られました。