Let's start Scheme

2012-10-17

ファイルの末尾に追加したい

ふと、R6RSの範囲でシェルで言う「>>」みたいなことができるのかなぁ?ということが気になった。

R6RSの範囲でファイルの末尾位置を取得する方法な無い(はずな)ので、outputポートのみを使うという方法は不可能である。となれば、input/outputポートを使用して、ポートを開いた直後に全部読み取ればポートの位置が末尾になるはず。ということで、こんなスクリプトで実験。
(import (rnrs))
(define out-file "out.txt")

(unless (file-exists? out-file)
  (call-with-output-file out-file
    (lambda (p) (put-string p "hello\n"))))

(let ((in/out (open-file-input/output-port
               out-file (file-options no-fail no-truncate))))
  (display (utf8->string (get-bytevector-all in/out)))
  (put-bytevector in/out (string->utf8 "hello\n"))
  (close-port in/out)) ;; これを忘れていた
#|
期待される出力結果。
1. 
 hello
2.
 hello
 hello

so on
|#
予定通りなら、期待される出力結果になって、末尾に追加されていることになる。とりあえず、Ypsilon、Mosh、Sagittarius、Chez、LarcenyとRacketで試してみた。

予定通りに動いた処理系:Sagittarius、Chez、Racket
全て上書き(helloを常に一個だけ出力)した処理系:Ypsilon、Mosh、Larceny

ふむ、ちょっとR6RSを読み返す必要があるらしい(またかよ・・・)。ということで、セクション8を読み返す。っで、面白いことに気づいた。以下面白い文章
(get-bytevector-all binary-input-port)    procedure 
Attempts to read all bytes until the next end of file, blocking as necessary. If one or more bytes are read, get-bytevector-all returns a bytevector containing all bytes up to the next end of file. Otherwise, get-bytevector-all returns the end-of-file object. The operation may block indefinitely waiting to see if more bytes will become available, even if some bytes are already available.
何が面白いか、どこにもポートポジションを更新すると書いてない\(^O^)/
他のget-bytevector関連は更新すると書いてあるのになぁ・・・ということで、スクリプトのget-bytevector-allの部分をget-bytevector-nに変更して512バイト読み取るようにした。
しかし、結果は変わらず。う~ん、これってYpsilonとMoshのバグなのか、R6RS的には未定義なのかどっちなんだろう?でも、input/outputポートってこういう用途に使うんじゃないのか?

上記の検証は嘘であることが発覚した。単にポートの閉じ忘れで、ポートを閉じないと、Ypsilon、MoshとLarcenyでは溜め込んだバッファをflushしないだけだった。逆にいうと、Chez、RacketとSagittariusではその辺に寛容でプログラム終了時(だと思う、SagittariusではGC時)にポートのflushを行っているというだけだろう。
恥ずかしい検証をしたけど、自分への戒めとして残しておくことにしよう・・・

No comments:

Post a Comment