データを読み書きする


Tags: R6RS, 入出力

R6RS 4.3 節に <datum> として説明されている値は read/writeget-datum/put-datum)によって equal? において等しいオブジェクトとして読み書きできる。このような性質を Read/Write Invariance (読み書き不変性)と言う。この性質のおかげで、 Lisp ではプログラム中で必要なデータ構造の多くを、直接手で書き下したり、簡単にファイルに保存したりすることができる。

<datum> → <lexeme datum>
         | <compound datum>
<lexeme datum> → <boolean> | <number>
         | <character> | <string> | <symbol>
<symbol> → <identifier>
<compound datum> → <list> | <vector> | <bytevector>
<list> → (<datum>*) | [<datum>*]
         | (<datum>+ . <datum>) | [<datum>+ . <datum>]
         | <abbreviation>
<abbreviation> → <abbrev prefix> <datum>
<abbrev prefix> → ’ | ‘ | , | ,@
         | #’ | #‘ | #, | #,@
<vector> → #(<datum>*)
<bytevector> → #vu8(<u8>*)
<u8> → <{0, ..., 255} の範囲の正確な整数を表現する任意の <number>>

R6RS 4.3 Datum syntax より

この事実を次のような簡単な手続きとマクロを書いて確かめてみる。手続き read-write-invariant? は、受け取ったオブジェクトを文字列ポートに write し、それを read したものがもとのオブジェクトと equal? であるかどうかを返す。 test マクロは結果確認用のユーティリティである。

(import (rnrs))

(define (read-write-invariant? obj)
  (call-with-port (open-string-input-port
                   (call-with-string-output-port
                    (lambda (p) (write obj p))))
    (lambda (p) (equal? obj (read p)))))

(define-syntax test
  (syntax-rules ()
    ((_ obj)
     (begin
       (write obj)
       (display ": ")
       (display (read-write-invariant? obj))
       (newline)))))

テスト結果、

(test #f)                               ; -> #f: #t
(test 1/2)                              ; -> 1/2: #t
(test -12)                              ; -> -12: #t
(test 2+5i)                             ; -> 2+5i #t
(test 3.141592653589793)                ; -> 3.141592653589793: #t
(test +inf.0)                           ; -> +inf.0: #t
(test +nan.0)                           ; -> +nan.0: #t
(test #\nul)                            ; -> #\nul: #t
(test #\linefeed)                       ; -> #\linefeed: #t
(test #\vtab)                           ; -> #\vtab: #t
(test #\page)                           ; -> #\page: #t
(test #\return)                         ; -> #\return: #t
(test #\esc)                            ; -> #\esc: #t
(test #\delete)                         ; -> #\delete: #t
(test "\a")                             ; -> "\a": #t
(test "\b")                             ; -> "\b": #t
(test "\t")                             ; -> "\t": #t
(test "\v")                             ; -> "\v": #t
(test "\r")                             ; -> "\r": #t
(test "ab\
 c")                                    ; -> "abc": #t
(test 'abc)                             ; -> abc: #t
(test '\x40;)                           ; -> \x40;: #t
(test 'a\x20;c)                         ; -> a\x20;c: #t
(test '(1 2 3))                         ; -> (1 2 3): #t
(test '(1 #\2 "3"))                     ; -> (1 #\2 "3"): #t
(test '#(a b c))                        ; -> #(a b c): #t
(test '#((a b) c))                      ; -> #((a b) c): #t
(test #vu8(0 128 255))                  ; -> #vu8(0 128 255): #t