Common Lispのarray

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

こういう結果が得られます。 各LineBufferbufは大元のall_bufferを指しているだけです。 (なのでall_bufferを変更するとLineBufferにあるbufも変わる。同じアドレスを指しているので)

こういうのをCommonLispでやるには、make-arrayでオプションを指定するとのことでした。

make-array

http://clhs.lisp.se/Body/f_mk_ar.htm

これの displaced-todisplaced-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版と同じ結果が得られました。

関連記事

comments powered by Disqus