Guile基礎/テキストファイルの入出力(コア)

変更履歴

概 要

目 次

参考資料

write,display,newline,simple-format(format)

procedure:
(write obj [port])
obj 任意のオブジェクト
port 出力ポート.省略時はカレント出力ポートに出力する.
返り値 unspecifed

注意 上記の角括弧 [ ... ] は省略可能であることを示しています. 手続き呼び出しの構文要素ではありません.

この手続きは,obj の外部表現を port に出力します.port が省略されたときにはカレント出力ポートに出力します.

obj の外部表現は,read 手続きが読み取り可能な形式になります. 例えば,文字列はダブルクォートで囲まれた形式で出力され, 文字列内のエスケープ文字はそのまま出力されます(エスケープ文字は機能しません). また,文字は #\ が付いた形式で出力されます.以下に,簡単な実行例を示します.
guile> (write #\a) (newline)
#\a
guile> (write "abc\tABC\n") (newline)
"abc\tABC\n"
guile> (write 'symbol) (newline)
symbol
guile> (write 123.456) (newline)
123.456
guile> (write '(a "a" #\a)) (newline)
(a "a" #\a)

procedure:
(display obj [port])
obj 任意のオブジェクト
port 出力ポート.省略時はカレント出力ポートに出力する.
返り値 unspecified

注意 上記の角括弧 [ ... ] は省略可能であることを示しています. 手続き呼び出しの構文要素ではありません.

この手続きは,obj の表現を port に出力します.port が省略されたときにはカレント出力ポートに出力します.

obj の表現は,人間が自然に読める形式で出力されます. 例えば,文字列はダブルクォートなしで出力され, 文字列内のエスケープ文字は機能を発揮します. また,文字は #\ のない形式(つまり,文字だけ)が出力されます. 以下に,簡単な実行例を示します.
guile> (display #\a) (newline)
a
guile> (display "abc\tABC\n") (newline)
abc	ABC

guile> (display 'symbol) (newline)
symbol
guile> (display 123.456) (newline)
123.456
guile> (display '(a "a" #\a)) (newline)
(a a a)
上の2番目の文字列("abc\tABC\n")を出力する例では,タブ文字(\t)と改行文字(\n)が,出力の際に機能を発揮しています.abc と ABC が離れているのはタブ文字(\t)が機能したためです.また,出力結果の直後が空行になっているのは改行文字(\n)が機能して,さらに newline を実行したためです.さらに,最後の結果を見ると,シンボルの 'a と文字列の "a" と文字の #\a の出力結果は見分けが付きません.

procedure:
(newline [port])
port 出力ポート.省略時はカレント出力ポートに出力する.
返り値 unspecified

注意 上記の角括弧 [ ... ] は省略可能であることを示しています. 手続き呼び出しの構文要素ではありません.

この手続きは,改行文字を port に出力します.port が省略されたときにはカレント出力ポートに出力します.

procedure:
(simple-format port fmt obj*)
(format port fmt obj*)
port 出力ポート または ブール値
fmt 文字列(書式指定)
obj 任意のオブジェクト
返り値 ブール値または文字列
備考 format 手続きについて,下記の補足を参照して下さい.

これらの手続きは,オブジェクトの列 obj* を書式指定 fmt に沿って port に出力します.port に #t を指定した場合,カレント出力ポートに出力します.一方,#f を指定した場合, 書式指定に沿った出力を文字列として返します.出力を行った場合,出力が成功したら #t を返します.出力に失敗したら #f を返すだろうと推測されますが,たいていエラーが発生します.これまでのところ #f が返ってくるのを経験したことはありません.

書式指定の文字列は,\n や \t などのような文字列データとしてのエスケープ文字に加えて,obj を表示するための書式指定用のエスケープ(下表)を含むことができます.
エスケープ 意味
~A
~a
対応する obj を display 手続きと同じ形式で表示します.
~S
~s
対応する obj を write 手続きと同じ形式で表示します.
~% 改行を表示します(文字列エスケープの \n と同じです).
~~ ティルダ(~)を表示します.

書式指定文字列の中の ~A と ~S の個数と obj の個数は一致していなければいけません.そうでないとエラーが発生します.

具体例

以下では,数値,文字,文字列,シンボルに関して ~A と ~S の違いを示しています. 文字と文字列以外には出力形式に違いはないように思います.
guile> (simple-format #t "~A ~S\n" 123.45 123.45)
123.45 123.45
guile> (simple-format #t "~A ~S\n" #\a #\a)
a #\a
guile> (simple-format #t "~a ~s\n" "Guile" "Guile")
Guile "Guile"
guile> (simple-format #t "~a ~s\n" 'Guile 'Guile)
Guile Guile

以下では,port に #f を指定した場合の結果を示します. #f を指定した場合,書式指定に沿った出力が文字列として返ってきます.
guile> (simple-format #f "~A ~S\n" 123.45 123.45)
$1 = "123.45 123.45\n"
guile> (simple-format #f "~A ~S\n" #\a #\a)
$2 = "a #\\a\n"
guile> (simple-format #f "~a ~s\n" "Guile" "Guile")
$3 = "Guile \"Guile\"\n"
guile> (simple-format #f "~a ~s\n" 'Guile 'Guile)
$4 = "Guile Guile\n"
なお,format 手続きでも同じことができます.

補足

Guileの起動時に実行される boot-9.scm の中で format は simple-format に束縛されます.ところが, システム起動直後から (ice-9 format) モジュールで定義されている高機能版の format手続き が利用できます.この点を確認するため,以下では,REPLの起動直後に, 浮動小数点数の表示を simple-format と format で試してみています.simple-format はその機能がないので失敗しています.
$ guile
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> (simple-format #t "~f\n" 123.45)
;;; <stdin>:1:0: warning: "~f\\n": unsupported format option ~f, use (ice-9 format) instead
In procedure simple-format: FORMAT: Unsupported format option ~f - use (ice-9 format) instead
guile> (format #t "~f\n" 123.45)
123.45
$1 = #t
つまり,Guileを起動する際のどこかのタイミングで format を再束縛しているものと思われます.(ice-9 format) モジュールの最後の行に
(module-set! the-root-module 'format format)
という式があって,これが関係しているのかも知れません.

simple-format は,boot-9.scm の中で(formatと名前を変えて)使われています. これから考えて,おそらく起動時のメッセージ表示のために作ったものだろうと推測します.simple-format の format に対する利点は,たぶん,速いことだけです.

補足

(ice-9 format) モジュールの format 手続きについてさらに補足します.上で示したように REPL では高機能版の format 手続きが使えるようです.でも,スクリプトでは使えたり使えなかったりしています.使用の可否が安定していないのですが,その理由は不明です.高機能版の format を使うときには,やはり (ice-9 format) モジュールをロードしたほうがよいでしょう.

end-of-file オブジェクト

このあとに示す read 手続きなどの入力用の手続きは, ファイルの終端(end of file)に到達したら end-of-file オブジェクト を返します.入力用手続きの返り値が end-of-file オブジェクトか否かを判定するためには次の手続きを使用します.

procedure:
(eof-object? obj)
obj 任意のオブジェクト
返り値 ブール値

この手続きは,obj が end-of-file オブジェクトのとき #t を返し, そうでないとき #f を返します.

read

procedure:
(read [port])
port 入力ポート.省略時はカレント入力ポートから入力する.
返り値 データ(datum)

注意 上記の角括弧 [ ... ] は省略可能であることを示しています. 手続き呼び出しの構文要素ではありません.

Guileデータの外部表現を port から入力して, それが表現するデータそのものを返します. port が省略されたときには,カレント入力ポート(current input port)から入力します.

例えば,文字データの外部表現 #\\$c$ を入力したときには文字データを返し, 文字列データの外部表現 " ... " を入力したときには文字列データを返し, リストデータの外部表現 ( ... ) を入力したときにはリストデータを返します. その他にも,Guileが扱うデータで外部表現を持つものはすべて入力できるはずです.

外部表現を入力したあと,入力した外部表現の直後までファイルポインタを移動します. ただし,入力時点でファイルの終端に到達していたときには enf of file オブジェクトを返します.

具体例

以下のプログラムは,コマンドライン引数に指定されたファイル(filename)からGuileデータの外部表現を入力して,そのデータを型とともに表示します.(srfi srfi-1) モジュールは,SRFI-1[Searching] で定義されている any 手続きを使うためにロードしています.
;; show-type.scm
(use-modules (srfi srfi-1))

(define *type-list* 
  (list number? char? string? symbol? keyword? list? pair? vector?))

(define (check-type datum) 
  (lambda (type?) (and (type? datum) (procedure-name type?))))

(let ((filename (cadr (command-line))))
  (call-with-input-file filename
    (lambda (port)
      (let loop ((datum (read port)))
        (unless (eof-object? datum)
          (format #t "~A: ~A\n" 
                  datum 
                  (or (any (check-type datum) *type-list*) "other type"))
          (loop (read port)))))))
以下の実行例は,Guileデータを適当に含んだファイル(data.txt)に対して上記のプログラムを実行しています.
$ cat data.txt
123.456  #\あ  "ガイル"  Guile  #:kwd  (a . b)  (1 2 3 4)  #(1 2 3)  #*0011
$ guile -s show-type.scm data.txt 
      ...... コンパイルメッセージ ......
123.456: number?
あ: char?
ガイル: string?
Guile: symbol?
#:kwd: keyword?
(a . b): pair?
(1 2 3 4): list?
#(1 2 3): vector?
#*0011: other type

補足

標準入力に対して read を実行したとき,Ctrl-D を押下すると read は end of file オブジェクトを返します.以下は,REPLにおいて実行した read に対して Ctrl-D を押下した場合を示しています.
guile> (read)
 (ここで Ctrl-D を押下しています)
$1 = #<eof>
(おしまい)