For Gauche 0.9.15Search (procedure/syntax/module):

Next: , Previous: , Up: 組み込みライブラリ   [Contents][Index]

6.21 入出力


6.21.1 ポート

Builtin Class: <port>

Portは、Schemeにおいて抽象化された入出力のインタフェースを提供します。 Gaucheはportにいくつかの拡張を行い、いろいろなアプリケーションに対応できるようにしました。

テキストとバイナリI/O

R7RSはテキストポートとバイナリポートを定義しています。 Gaucheでは、ほとんどのポートはテキストI/OとバイナリI/Oを混ぜて使えます。 概念的には、異なる型のオブジェクトのソース/シンクは別物と考える方が綺麗ですし、 ひとつのポートでテキストI/OとバイナリI/Oを混在して使うことは滅多にありません。

しかし現実には、ポートを型の無いデータの集まりにつながったものとして、 データをどう解釈するかは後から決めたい、という場合があります。 例えば標準入出力です。Unix的な環境では、あらかじめオープンされている標準入出力 に対してテキストI/Oを行うかバイナリI/Oを行うかはプログラムに任されています。 R7RSでは、current-input-port等の初期値はテキストポートであると 規定しています。Gaucheではどちらにも使うことができます。

変換

ポートはまた、データストリームを変換するのにも使えます。例えばgauche.charconv モジュールでは、文字コード間の変換を行うポートを提供しています (詳しくはgauche.charconv - 文字コード変換を参照)。 SRFI-181もこの機能を提供します(符号変換ポート参照)。

追加機能

また、特殊な機能を実現するポートもあります。 コーディング認識ポート(コーディング認識ポート参照)は ファイル中の特殊なコメントを認識して、そのファイルがどの文字エンコーディング で書かれているかを検出します。 仮想ポート(gauche.vport - 仮想ポート参照)はSchemeでふるまいをプログラムできる ポートを提供します。同様の機能はSRFI-181にもあります (srfi.181 - カスタムポート)。

ポートを組み合わせて通信チャネルを作ることもできます。 control.plumbing - ポートの配管で説明しています。


6.21.2 ポートとスレッド

組み込みのポート操作関数は内部的にポートをロックして、 複数のスレッドからの同一のポートへのアクセスがポートの内部状態を壊さないように しています。 (SRFI-18によって要求されている動作です)。 ここで「組み込みのポート操作関数」はGaucheにより提供される、 ポートを引数に取り何らかのI/O動作や問い合わせを行う手続きで、 read/writeread-char/write-charport->string等を含みます。 但し、call-with-*with-*系関数は、 与えられた手続きを呼ぶ際にはポートをロックしません。 その手続きが別のスレッドにポートを渡すかもしれず、Gaucheにはそれを知ることが できないからです。

従って、マルチスレッド環境でポートへのアクセス競合により ポートの内部状態を壊してしまうんじゃないか、などとあまり神経質に なる必要はありません。但し、このロック機構はあくまで予想外の アクセス競合によってポートがおかしな状態になってしまうことを 防ぐための安全ネットであって、一般的な排他制御機構として使われる ことは想定していないのに注意して下さい。このロックの実装は、 ポートへのアクセス競合は例外的な場合のみであると仮定し、 通常のアクセスにおけるオーバヘッドを避けるために、スピンロックを 使用します。もし、意図的にポートアクセスが競合するようなコードを書く場合は、 明示的に排他制御をしてください。

Function: with-port-locking port thunk

portをロックし、thunkを実行します。 ロックはthunkのダイナミックエクステントの期間有効です。

portがロックされている期間での組み込みのポートアクセス関数の 呼び出しは排他制御をバイパスするため、性能向上が見込まれます。

ロックの有効期間はthunkのダイナミックエクステントなので、 thunk内からwith-port-lockingの外で捕捉された 継続を呼んだ場合、ロックは解放されます。その後、thunk内で 捕捉された継続が呼ばれた場合、再びロックが獲得されます。

with-port-lockingはネスト可能です。ロックは最も外側の with-port-lockingの期間中有効となります。

この手続きはポート組込みのロック機構を利用します。つまり、ポートアクセスが 競合した場合はbusy waitになるということです。この手続きはあくまで 頻繁なロックによるオーバヘッドを回避するためのものです。 もし本当に競合が予測される場合は明示的に排他制御を行ってください。


6.21.3 ポート共通の操作

Function: port? obj
Function: input-port? obj
Function: output-port? obj

[R7RS base] obj がそれぞれポート、入力ポート、出力ポートなら真を返します。 port?はR5RSの"Standard Procedures"の項には 載っていませんが、"Disjointness of Types"の項に挙げられています。

Function: port-closed? port

objがポートであり、既に閉じられていた場合に真を返します。 一度閉じたポートは再び開くことはできません。

Parameter: current-input-port
Parameter: current-output-port
Parameter: current-error-port

[R7RS base] 現在の入力ポート、出力ポート、エラー出力ポートをそれぞれ返します。

R7RSではこれらのポートの初期値はテキストポートであると規定されています。 Gaucheでは、これらのポートの初期値はテキスト入出力もバイナリ入出力も扱えます。

現在のポートの値はparameterizeを使って一時的に変更できます (パラメータ参照)。もっとも、典型的な場合には より簡単なwith-output-to-stringwith-input-from-fileなどの 手続きが使えるでしょう。

(let1 os (open-output-string)
  (parameterize ((current-output-port os))
    (display "foo"))
  (get-output-string os))
 ⇒ "foo"
Parameter: current-trace-port

デバッグトレース出力に使われる出力ポートを保持するパラメータです。 初期値はcurrent-error-portの初期値と同じです。

debug-print機能(デバッグ補助参照)と、 マクロトレース機能(マクロ展開をトレースする参照)がこのポートを使っています。

Parameter: standard-input-port
Parameter: standard-output-port
Parameter: standard-error-port

プログラム開始時点の標準入出力ポートを返します。これらの値が current-input-portcurrent-output-portcurrent-error-portのデフォルトとなります。

これらの手続きの値もparameterizeで変えることができますが、 (1) current-*-portはプログラム実行前に初期化されるので、 プログラム中でstandard-*-portを変更しても影響は及ばない、 (2) これらの手続きの値の変更はScheme世界だけのことで、低レベルライブラリが 参照しているシステムのstdioファイルディスクリプタは変更されない、 という点に注意してください。

Function: with-input-from-port port thunk
Function: with-output-to-port port thunk
Function: with-error-to-port port thunk

それぞれ入力、出力、エラーポートをportにセットした状態でthunkを呼び出します。 portthunk実行後にも閉じられないことに注意してください。

Function: with-ports iport oport eport thunk

上の3つの動作を同時に行う手続きです。 入力、出力、エラーの各ポートをそれぞれiport, oport, eportに セットしてthunkを呼び出します。変更する必要がないポートの引数には #fを渡すことができます。

portthunk実行後にも閉じられないことに注意してください (最近のScheme標準は良く似た名前の手続きcall-with-portを追加しましたが、 生憎そちらはポートを閉じる仕様になっています。call-with-portについては 下で説明します。)

Function: close-port port
Function: close-input-port port
Function: close-output-port port

[R7RS base] ポートを閉じます。close-portは入力ポートも出力ポートも閉じられますが、 close-input-portclose-output-portはそれぞれ対応するポートのみに 使え、異なる種類のポートが渡されたらエラーを通知します。

理屈の上では、close-portだけあれば十分です。3つ手続きがあるのは歴史的理由です。 R5RSにはclose-input-port and close-output-portだけがありました。 R6RSとR7RSは3つ全てをサポートしています。

Function: call-with-port port proc

[R7RS base] procを、portを引数として呼び出します。procからリターンするか、 エラーが投げられた場合はportがクロースざれます。 procが返す値がそのままcall-with-portの返り値となります。

Function: port-type port

portのタイプを、シンボルfilestringprocの いずれかで返します。

Function: port-name port

portの名前を返します。ポートがファイルに関連付けられている場合は、ポートの名前は ファイル名です。そうでない場合、ポートを説明する文字列が返されます。

Function: port-buffering port
Function: (setter port-buffering) port buffering-mode

ファイルポート((port-type port)fileを返すもの) に対して、そのバッファリングモードを読みだし、もしくは変更します。 入力ポートではバッファリングモードは :full:modest:noneのいずれかです。 出力ポートでは :full:line:noneのいずれかです。 バッファリングモードの詳細な説明は、ファイルポート を参照してください。

port-bufferingがファイルポート以外のポートに対して呼ばれた場合は #fを返します。port-bufferingのsetterが ファイルポート以外のポートに対して呼ばれた場合はエラーとなります。

入力ポートと出力ポートをリンクすることができます。 リンクされている入力ポートから読み出そうとすると、まず出力ポートのバッファが フラッシュされます。ポートは2つ以上のポートとリンクすることはできません。

デフォルトでは、標準入力ポートと標準出力ポートがリンクされています。

port-link!は引数の入力ポートと出力ポートをリンクします。 どちらのポートもリンクされていない状態でなければなりません。 さもなくばエラーが投げられます。

port-unlink!は、与えられたポートが他のポートとリンクされていたら そのリンクを解除します。リンクされていないポートが与えられた場合は何もしません。

ポートのlinkスロットを見れば、ポートがリンクされているか、 またされている場合はどのポートか、がわかります。

Function: port-current-line port

portの現在の行番号を返します。行番号は、ファイルに関連付けられたポートで かつシーケンシャルなキャラクタI/Oを行っている場合のみ有効です。それ以外の場合は -1を返します。

Function: port-file-number port :optional dup?

portがファイルに関連付けられている場合、そのファイルディスクリプタ番号を 返します。それ以外の場合は#fを返します。

dup?引数に真の値が与えられると、この手続きはdup(2)を呼んで 複製したファイルディスクリプタを返します。この場合、返されたファイルディスクリプタは 呼び出し側が責任を持って閉じなければなりません (sys-closeが使えます)。

Function: port-position port

[SRFI-192] portの現在の位置を返します。現在の位置とは、 入力ポートでは次の読み込みでデータが読まれる位置、 出力ポートでは次の書き込みでデータが書かれる位置です。 (peek-char/peek-byteでは現在の位置は動きません)。

portが単純なファイルポートや文字列ポートなら、 ポートの位置はストリームの先頭からのバイトオフセットを表す正確な整数です。 より複雑なポート、例えば文字エンコーディング変換ポートでは、 ポート位置を取ることはできないか、取れた場合でもその値はバイトオフセットに対応するとは 限りません。 仮想ポート(gauche.vport - 仮想ポート参照)では、現在位置は任意のSchemeオブジェクトで、 set-port-position!に渡す以外に使うことはできません。

portがポート位置をサポートしていなければエラーが投げられます。 ポート位置をサポートしているかどうかはport-has-port-position?で調べられます。

ポータブルなコードを書くときは、以下のSRFI-192の条件に気をつけてください。

  • ポート位置は任意のSchemeオブジェクトであり、 同じポートに対してset-port-position!するのにだけ使うことができます。
  • ただし、ポートがバイナリポートで、位置が正確な非負整数の場合は、 それはバイトオフセットを表しています。
Function: port-has-port-position? port

[SRFI-192] ポートがport-position手続きをサポートしていれば#tを、 そうでなければ#fを返します。

Function: set-port-position! port pos

[SRFI-192] posportの現在の位置としてセットします。 現在の位置とは、 入力ポートでは次の読み込みでデータが読まれる位置、 出力ポートでは次の書き込みでデータが書かれる位置です。

posの解釈はportに依存します。一般的には、 portに対するport-positionで以前返されたオブジェクトしか渡せません。 ただし、portが単純なファイルポート(ファイルに対してオープンされ、 文字コード変換などを行っていないポート)や入力文字列ポートならば、 非負の正確な整数をバイトオフセットとして渡すことができます。

ポートが位置のセットを許さない場合や、posが位置として不正な場合は エラーが投げられます。portに位置がセットできるかどうかは port-has-set-port-position!?で検査できます。

Function: port-has-set-port-position!? port

[SRFI-192] ポートがset-port-position!手続きをサポートしていれば#tを、 そうでなければ#fを返します。

Function: port-seek port offset :optional whence

(この手続きは非推奨です。新しいコードにはport-positionおよび set-port-position!を使ってください)。

portがランダムアクセス可能なポートの場合、 この手続きはportのread/writeポインタをoffsetwhenceの値によって 設定し、新たなread/writeポインタの値(データの先頭からのバイトオフセット)を 返します。portがランダムアクセス可能でない場合は#fが返されます。 現在のバージョンでは、ファイルポートおよび入力文字列ポートがランダムアクセス可能です。 出力文字列ポートは現在のポインタの値を問い合わせる動作だけが可能です。

ポートのポインタはバイト数で表現され、文字数とは異なることに注意して下さい。

portが出力ファイルポートの場合は、データの終端を超えた位置までseek することが可能です。その場合の動作はPOSIXのlseek(2)に準じます。 入力ファイルポートや入力文字列ポートではデータの終端以降にseekすることはできません。

whence引数は、offsetの基準を指定する小さな整数です。 以下の定数が定義されています。

SEEK_SET

offsetはデータ先頭からのバイト数を指定します。 whenceが省略された場合のデフォルトの動作です。

SEEK_CUR

offsetは現在のread/writeポインタからの相対バイト数を指定します。 offsetが0であれば、ポインタを動かさずに現在のポート位置を知ることができます。

SEEK_END

offsetはデータの終端からの相対バイト数を指定します。

Function: port-tell port

portの現在のread/writeポインタの値をバイト数で返します。 portがランダムアクセス可能でない場合は#fが返されます。 これは以下の呼び出しと等価です。

(port-seek port 0 SEEK_CUR)

名前に関するメモ: port-seekは他の処理系で seekfile-positioninput-port-position/ output-port-position等と呼ばれています。 port-telltellftellset-file-position!等と 呼ばれています。いくつかの処理系はport-positionという手続きを 持っていますが、port-seekとは別の機能を実現しています。 file-positionはCommonLisp由来の名前ですが、 fileポート以外のものも扱うため採用しませんでした。 また、seektellはPOSIXの名前由来であり、 Gaucheの名前付け規則を使ってsys-seeksys-tellとしても よさそうですが、portの操作はシステムコールレベルよりも抽象度が高いため これも採用しませんでした。結局、新しい名前を採用することにしました。

Function: copy-port src dst :key (unit 0) (size #f)

srcからEOFまでデータを読みだし、dstへ書き出します。

キーワード引数unitは0以上の整数か、シンボルbyteもしくはchar でなければなりません。これはデータをコピーする単位を指定します。 整数ならば、その大きさ(0の場合はシステム規定の大きさ)のバッファが確保され、 ブロックI/Oを使って転送が行われます。通常のファイルをコピーする場合などはこれが 速いでしょう。もしunitがシンボルbyteであれば、バイト毎 に読みだし/書き込みが行われます。unitがシンボルcharであれば、 キャラクタ毎に読みだし/書き込みが行われます。

キーワード引数sizeに非負の整数が与えられた場合、それはコピーされるデータの 最大量を指定します。unitがシンボルcharの場合はsizeは コピーされる文字数を、そうでない場合はバイト数を指定します。

unitがシンボルcharの場合はコピーされた文字数を返し、 そうでない場合はコピーされたバイト数を返します。


6.21.4 ファイルポート


6.21.4.1 ファイルポートのオープン

Function: open-input-file filename :key if-does-not-exist buffering element-type encoding conversion-buffer-size conversion-illegal-output
Function: open-output-file filename :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size conversion-illegal-output

[R7RS+ file] ファイルfilenameを入力または出力用にオープンし、 入力ポートまたは出力ポートを作成して返します。

キーワード引数により、動作を細かく指定できます。

:if-exists

このキーワード引数はopen-output-fileのみに指定でき、 filenameが既に存在した場合の動作を指定します。次の値のいずれかを与えることができます。

:supersede

既存のファイルが長さ0に縮められます。これが既定の動作です。

:append

既存のファイルにこれから書き出す内容が追加されます。

:overwrite

既存のファイルにこれから書き出す内容が上書きされます。 書き出されるデータが既存のファイルのデータよりも短い場合、 残りの部分はそのまま残されます。

:error

エラーが報告されます。

#f

何もせず、#fを返します。

:if-does-not-exist

このキーワード引数はfilenameが存在しない場合の動作を指定します。

:error

エラーを報告します。これがopen-input-fileの既定の動作です。

:create

ファイルが作成されます。これがopen-output-fileの既定の動作です。 ファイルの存在のチェックと作成はアトミックに行われます。 このオプションに加え、if-existsオプションに:error#fを 指定することで、排他的にファイルを作成することができます。 open-input-fileに対してはこの値を指定することはできません。

#f

何もせず、#fを返します。

:buffering

この引数はバッファリングモードを指定します。以下の値が設定できます。 ポートのバッファリングモードは手続きport-buffering (ポート共通の操作参照)によって 読みだし/変更可能です。

:full

出来る限りデータをバッファリングします。これがデフォルトのモードです。

:none

バッファリングを行いません。出力ポートにデータが書き出されるか、 入力ポートからデータが読み込まれる度に、下位にあるシステムコールが呼ばれます。 プロセスの標準エラーポートはこのモードでオープンされています。

:line

このモードは出力ポートにのみ有効です。書き出されたデータはバッファに 貯められますが、改行文字が書かれたらフラッシュされます。 このモードは対話的な出力ポートなどに便利です。 プロセスの標準出力ポートはこのモードでオープンされています。 (これは、Cのstdioライブラリの「ラインバッファリング」とちょっと違うことに 注意してください。stdioでは同じファイルディスクリプタから入力が行われる時も バッファはフラッシュされますが、Gaucheではそうはなりません)。

:modest

このモードは入力ポートにのみ有効です。ほとんど:fullバッファリングモードと 同じですが、read-uvectorはポートに要求されたデータより少ないデータしか 無かった場合、要求された量がたまるまで待つのではなく、今あるデータだけを 返します(:fullの場合はread-uvectorはすべてのデータが到着するまで 待ちます)。このモードはポートがパイプやネットワークに接続されている場合に 便利です。

:element-type

この引数はファイルのタイプを指定します。

:binary

ファイルはバイナリモードでオープンされます。(これがデフォルトです。)

:character

ファイルはキャラクタモード(テキストモード)でオープンされます。

註: このフラグで違いが出るのはWindowsネイティブ版のみです。 キャラクタモードでは、#\newline文字をを出力するとCR + LFが書き出され、 またCR + LFシーケンスは単一の#\newlineとして読まれます。

Unixでは両者に違いはありません。

なお、Gaucheにはテキストポートとバイナリポートの区別はありません。 したがってこのフラグはWindowsの行終端文字の扱いのみのためにあります。

:encoding

この引数はファイルの文字エンコーディングを指定します。引数は文字列かシンボルで、 文字エンコーディングスキーム(CES)の名前を渡します。

open-input-fileでは、ここにワイルドカードCES (例: *jp) を 渡して、入力ファイルのエンコーディングを推測させることもできます (文字エンコーディングの自動判定参照)。 また、ここに#tを渡すと、入力ポートはCoding aware portでラップされます (コーディング認識ポート参照)。入力ファイルがエンコーディング指定の マジックコメントを持っていることが期待できる場合に便利です。

この引数が与えられた場合、Gaucheは自動的にgauche.charconvモジュールを ロードし、ポートの入出力時に文字コード変換を行います。 CESについて詳しくはサポートされる文字エンコーディングを参照してください。

この引数が省略された場合は、パラメータdefault-file-encoding の値が指定されたものとみなされます (ファイルI/Oにおける文字エンコーディング参照)。

:conversion-buffer-size

この引数は、文字エンコーディング変換に使うバッファサイズを指定するために encoding引数と共に使うことができます。渡された値はそのまま 文字コード変換ポートのコンストラクタのbuffer-size引数に渡されます (変換ポート参照)。

この引数を指定する必要は滅多にありませんが、入力ファイルの文字エンコーディングを 推測しなければならない場合、大きめのバッファサイズの方が精度が上がります。 推測ルーチンがより多くのデータを見て文字エンコーディングを決定できるからです。

:conversion-illegal-output

この引数はencode引数と一緒に使うことで、 文字エンコーディング変換において、元の文字に対応する文字が変換先に無い場合の振る舞いを 指定できます。値はシンボルraisereplaceでなければならず、 それはそのまま変換ポートのillegal-output引数に渡されます。 詳しくは変換ポートを参照してください。

if-existsif-does-not-existフラグの組合せにより、 色々な動作を実現できます。

(open-output-file "foo" :if-exists :error)
 ⇒ ;"foo"を排他的にオープンするかエラーを報告する

(open-output-file "foo" :if-exists #f)
 ⇒ ;"foo"を排他的にオープンするか#fを返す

(open-output-file "foo" :if-exists :append
                        :if-does-not-exist :error)
 ⇒ ;"foo"が既に存在する場合に限り、それを追加モードでオープン

ファイルをオープンせずにその存在をチェックするには、 sys-accessfile-exists?を使って下さい (ファイルの状態参照)。

移植性に関する註:Schemeシステムによっては、filenameのところに シェルコマンドを指定して、サブプロセスの標準入出力と通信できるようにするものが あります。他のスクリプティング言語(例:Perl)にも同様の機能があります。 Gaucheでは、open-input-fileopen-output-fileは あくまでファイル (OSがファイルとして扱うもの) のみに対して使えます。 サブプロセスと通信するためには、「プロセスポート」という機能が提供されています。 Process portsを参照して下さい。

Function: call-with-input-file string proc :key if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: call-with-output-file string proc :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R7RS+ file] stringで示されるファイルを入力または出力用にオープンし、 作成されたポートを引数として手続きprocを呼び出します。 procが正常終了するか、proc内で捕捉されないエラーが起きた場合に ファイルはクローズされます。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 if-existsif-does-not-exist#fを指定した場合、 ファイルがオープンされなかった場合はprocにポートではなく#fが渡される ことに注意して下さい。

procが返す値を返します。

Function: with-input-from-file string thunk :key if-does-not-exist buffering element-type encoding conversion-buffer-size
Function: with-output-to-file string thunk :key if-does-not-exist if-exists buffering element-type encoding conversion-buffer-size

[R7RS file] stringで示されるファイルを入力または出力用にオープンし、オープンされた ポートを現在の入力または出力ポートに設定して、thunkを呼び出します。 thunkが戻るか、thunk内で捕捉されないエラーが生じた際にファイルは閉じられます。

thunkが返す値を返します。

キーワード引数は open-input-file及びopen-output-fileのものと同じ意味を持ちます。 但しif-existsif-does-not-exist#fが指定され、 ファイルがオープンできなかった場合は、thunkは呼ばれずに 直ちに#fが返されます。

ポートを閉じるセマンティクスについて: R7RSはcall-with-port等の説明において、次のように述べています。 「procが戻って来なかった場合、今後ポートが読み書きに一切使われないことが 証明できない限りは、ポートは自動的には閉じられない」。

Gaucheの実装は若干この条件には反しています。捕捉されないエラーがprocから 発せられたというだけでは、そのポートが今後一切使われないかどうかはわかりません。 しかし実際には、そのようなエラーが発せられた後でポートに対して意味のある操作をするのは 非現実的です。ポートがどのような状態にあるかわからないわけですから。 現実的なプログラムでは、ポートに対して意味のある操作をしつづけたいのなら、 procの中で明示的にエラーをハンドルすべきでしょう。

call-with-input-fileの外で捕捉された継続をproc内で呼んだ場合には ポートは閉じられないことに注意して下さい。後でprocへと制御が戻ってくるかも しれないからです (コルーチン等)。また、 低レベルの例外メカニズム(下位レベルの例外処理機構 参照)を利用した場合、エラー時にポートを閉じるのはプログラマの責任になります。


6.21.4.2 ファイルI/Oにおける文字エンコーディング

Parameter: default-file-encoding

ファイルポートをオープンする、open-input-portcall-with-output-file 等の手続きにencodingキーワード引数を指定しなかった場合、 このパラメータの値が文字エンコーディング名として使われます。 デフォルトの値はutf-8です。

ほとんどの場合、このパラメータの値を変える必要はないでしょう。 データ交換にはutf-8がデファクトとなった現代では、utf8でないデータを 扱う時のみencodingを指定するだけで事足ります。

ただ、例えばこれまでGaucheの内部エンコーディングをnoneでコンパイルしていて (これはGaucheが文字を8ビット固定長で扱うことを意味します)、システムがLatin-1エンコーディング でデータを扱っていたとしましょう。 Gaucheは1.0から内部エンコーディングをutf-8のみにしたので、 バージョンを上げる際に、 全てのファイルオープン手続きを調べて:encoding latin-1をつけて回らないとなりません。 それはかなり大きな変更となるでしょう。

そういう場合には、このパラメータをlatin1にセットすることで、 これまで:encodingがついていなかったファイルオープン手続きを あたかも:encoding latin-1が付加されているかのように振る舞わせることができます。

ソースコードをloadする場合は、内部エンコーディングをデフォルトとする コーディング認識ポートが使われることに注意してください (コーディング認識ポート参照)。 内部エンコーディングとソースエンコーディングが異なる場合は、 文字エンコーディングを指定する呪文コメントcoding:coding-nameを使ってください。


6.21.4.3 ファイルディスクリプタポート

Function: open-input-fd-port fd :key buffering name owner?
Function: open-output-fd-port fd :key buffering name owner?

与えられたファイルディスクリプタにアクセスする入力または出力ポートを 作成して返します。bufferingopen-input-file の項で 説明されたポートのバッファリングモードを指定します。デフォルトは:fullです。 nameport-nameによって返されるポートの名前を指定します。

owner? は、このポートを閉じた時にfdもクローズすべきかどうかを 指定します。#fならポートを閉じてもfdはそのままです。 #tならポートを閉じた時にfdも自動的に閉じられます。 また、シンボルdupを渡すこともできて、その場合は内部でfddup(2)システムコールによって複製され、新しいfdをポートが所有します。 新たなfdはポートが閉じると自動的に閉じられますが、fd自体は閉じられません。 また、fdをポートとは独立して閉じることができます。

Function: port-fd-dup! toport fromport

システムのdup2(2)のインタフェースです。 アトミックにtoportのファイルディスクリプタをクローズし、fromportの ファイルディスクリプタを複製したものをtoportに設定します。 toportfromportはいずれもファイルポートでなければなりません。

ファイルディスクリプタが「複製」されると、ふたつのディスクリプタ番号が異なっていても それらはシステムのオープンファイルテーブルの同じエントリを指します。 例えば、現在の(システムレベルでの)ファイル上の読み書き位置は共有されます。 port-fd-dup!の後で、port-seekfromportに 対して呼び出せば、その変更はtoportの読み書き位置にも影響を与えるでしょうし、 その逆もまたあります。ただし、共有されるのはシステムレベルの情報のみで、 toportfromportがバッファリングされている場合、バッファの内容は 共有されません。

この手続きは、主にファイルディスクリプタを明示的に制御する必要のあるプログラム のために用意されています。例えばデーモンプロセスがその入出力を/dev/nullなどの 無難なデバイスに切り替えたり、シェルプロセスが子プロセスをexecする前に そのファイルディスクリプタをセットアップしたりするような場合です。


6.21.5 文字列ポート

文字列ポートは、メモリ上のデータと関連付けられたポートです。

Function: open-input-string string :key name

[R7RS base][SRFI-6] stringを内容とする入力文字列ポートを作って返します。 文字列に逐次的にアクセスする場合、インデックスをインクリメントしながら string-refを呼び出すより効率の良い方法です。

(define p (open-input-string "文字 列"))
(read p) ⇒ 文字
(read-char p) ⇒ #\space
(read-char p) ⇒ #\列
(read-char p) ⇒ #<eof>
(read-char p) ⇒ #<eof>

nameキーワード引数はGaucheの拡張です。 デフォルトでは、作られるポートの名前は(input string port)になります。 ポートの名前は主にデバッグのために使われます。この引数で代わりの名前を指定できます。 Gaucheの慣習として、ファイルポートは元ファイルのパス名を名前に持つので、 ポート名にデバッグ用途の情報を載せる場合はパス名と区別しやすいよう括弧に入れるのが良いでしょう。

gosh> (open-input-string "")
#<iport (input string port) 0x215c0c0>
gosh> (open-input-string "" :name "(user input)")
#<iport (user input) 0x22a4e40>
Function: get-remaining-input-string port

portは入力文字列ポートでなければなりません。 入力ポートに残っている文字列を返します。 portの内部ポインタは動かされないので、portに対するreadは 影響を受けません。portが既にEOFに達していた場合は、空文字列が返されます。

(define p (open-input-string "abc\ndef"))
(read-line p)                  ⇒ "abc"
(get-remaining-input-string p) ⇒ "def"
(read-char p)                  ⇒ #\d
(read-line p)                  ⇒ "ef"
(get-remaining-input-string p) ⇒ ""
Function: open-output-string :key name

[R7RS base][SRFI-6] 出力文字列ポートを作成して返します。このポートに書き出された文字列は 内部のバッファにたくわえられ、get-output-string で取り出すことが できます。 これは、順番に文字列を構成する方法として、あらかじめ文字列をアロケートして string-set!で埋めて行くよりもずっと効率の良い方法です。

nameキーワード引数はGaucheの拡張です。 デフォルトでは、作られるポートの名前は(output string port)になります。 ポートの名前は主にデバッグのために使われます。この引数で代わりの名前を指定できます。 Gaucheの慣習として、ファイルポートは元ファイルのパス名を名前に持つので、 ポート名にデバッグ用途の情報を載せる場合はパス名と区別しやすいよう括弧に入れるのが良いでしょう。

gosh> (open-output-string)
#<oport (output string port) 0x22a4c00>
gosh> (open-output-string :name "(temporary output)")
#<oport (temporary output) 0x22a49c0>
Function: get-output-string port

[R7RS base][SRFI-6] 出力文字列ポートportを取り、それまでそのポートに蓄積された 文字列を返します。バイトデータがそのポートに書き出されていた場合、 この手続きはまず内部バッファをスキャンし、結果が完全な文字列で表現できるかどうかを 調べます。もし表現できなければ、不完全な文字列が返されます。

これはportの操作には影響をあたえません。get-output-stringを 呼んだ後でも、portに内容を蓄積しつづけることができます。

Function: call-with-input-string string proc
Function: call-with-output-string proc
Function: with-input-from-string string thunk
Function: with-output-to-string thunk

これらのユーティリティ関数は次に定義されるような動作をします。 インタフェースはファイルポートを扱う類似の関数と揃えてあります。

(define (call-with-output-string proc)
  (let ((out (open-output-string)))
    (proc out)
    (get-output-string out)))

(define (call-with-input-string str proc)
  (let ((in (open-input-string str)))
    (proc in)))

(define (with-output-to-string thunk)
  (let ((out (open-output-string)))
    (with-output-to-port out thunk)
    (get-output-string out)))

(define (with-input-from-string str thunk)
  (with-input-from-port (open-input-string str) thunk))
Function: call-with-string-io str proc
Function: with-string-io str thunk
(define (call-with-string-io str proc)
  (let ((out (open-output-string))
        (in  (open-input-string str)))
    (proc in out)
    (get-output-string out)))

(define (with-string-io str thunk)
  (with-output-to-string
    (lambda ()
      (with-input-from-string str
        thunk))))
Function: write-to-string obj :optional writer
Function: read-from-string string :optional start end

文字列ポートを使う定型句をユーティリティ関数にしました。

(write-to-string obj writer)
  ≡
  (with-output-to-string (lambda () (writer obj)))

(read-from-string string)
  ≡
  (with-input-from-string string read)

writerの既定値はwriteです。start, endは 省略されればそれぞれ0と文字列の長さになります。

移植性への註:Common Lispに同名の関数があります。必須引数の動作は同じですが、 省略可能な引数は異なります。 STkにはread-from-stringがありますが、省略可能な引数は取りません。


Next: , Previous: , Up: 入出力   [Contents][Index]

6.21.6 コーディング認識ポート

コーディング認識ポートは特殊な手続的入力ポートで、loadが プログラムソースコードを読む際に使われています。このポートは ;; -*- coding: utf-8 -*-のような、プログラムソースの 文字エンコーディングを指定する特殊なコメントを認識し、適切な 文字エンコーディング変換を行います。 特殊なコメントでソースの文字エンコーディングを指定することについては、 マルチバイトスクリプトを参照して下さい。

Function: open-coding-aware-port iport

入力ポートを引数としてとりコーディング認識入力ポートを返します。 基本的には iport からの入力データをリーダにわたしているだけです。 しかし、iport からの入力データの最初の2行以内に、特別な呪文コメント が現れた場合、コーディング認識ポートは、その後に読み込まれるデータについて 必要な文字エンコーディング変換を行います。

引数として渡されたポート、iport は生成されたコーディング認識 ポートによって所有されます。つまり、コーディング認識ポートがクローズ されると、iport もクローズされます。iport から読み込まれた 内容はコーディング認識ポート内でバッファリングさます、したがって、 別のコードで iport から読み出しを行うべきではありません。

デフォルトでは、Gauche の load はプログラムソースを読むのに コーディング認識ポートを使います。したがって、文字エンコーディングを 示す特別な呪文コメントは、Gauche のソースプログラムでは有効になります (Schemeファイルのロード参照)。ただし、この機構自身は load とは 独立しており、このポートを別の目的で利用できます。特にコーディングの 呪文コメントがある Scheme のソースプログラムを処理する関数を書くときに 便利です。


6.21.7 入力

入力に関する手続きで、省略可能な引数iportは入力ポートでなければなりません。 省略された場合が現在の入力ポートが使われます。


6.21.7.1 データの読み込み

Function: read :optional iport

[R7RS base] iportからS式をひとつ読み込んで返します。 GaucheはR7RSに定義されている構文要素に加え、字句構造に 定義されている拡張構文要素を認識します。

iportが既にEOFに達していた場合は、EOFオブジェクトが返されます。

この手続きはS式を構成する最後の文字までを読み、その後の文字はポートに 残します。これは、S式に続く空白文字も読み込むCommonLispのreadの 振る舞いとは異なります。

Function: read-with-shared-structure :optional iport
Function: read/ss :optional iport

[SRFI-38] これらの手続きは、SRFI-38 で定義されていて、共有構造を表す記法 (#n=, #n#)を認識できます。Gauche の組み込み read は この SRFI-38 の記法を認識します。それゆえ、これらの手続きは、read と同じで、SRFI-38 との互換性のために用意されています。

Function: read-char :optional iport

[R7RS base] iportから1文字読み込んで返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: peek-char :optional iport

[R7RS base] iportから1文字読み込んで返します。文字はそのままiportに留まります。 iportが既にEOFに達していた場合はeofオブジェクトを返します。 iportにあるバイトストリームが正しい文字を構成しない場合、 ふるまいは未定義です。(将来はポート側に、不正な文字に対する対応を決める オプションを設ける予定です)。

Function: read-byte :optional iport
Function: read-u8 :optional iport

[R7RS base] 入力ポートiportから1バイト読み込み、0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

これは伝統的にread-byteと呼ばれてきましたが、 R7RSではread-u8と名づけています。どちらも使えます。

Function: peek-byte :optional iport
Function: peek-u8 :optional iport

[R7RS base] 入力ポートiportの先頭の1バイトを見て、それを0から255までの整数値として返します。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

これは伝統的にpeek-byteと呼ばれてきましたが、 R7RSではpeek-u8と名づけています。どちらも使えます。

Function: read-line :optional iport allow-byte-string?

[R7RS base] 入力ポートから、行末もしくはEOFまで読み込んで文字列として返します。 よく使われる行末 (LF only, CRLF, and CR only) を認識します。 戻り値にはこれらの行末文字は含まれません。 iportが既にEOFに達していた場合はeofオブジェクトを返します。

iportから、内部文字エンコーディングでは文字を構成し得ないバイトシーケンスが 読まれた場合、デフォルトではread-lineはエラーを通知します。 しかし、省略可能な引数allow-byte-string?に真の値が与えられた場合は、 read-lineはエラーを通知せず、かわりにバイト文字列 (不完全な文字列) を 返します。この動作は、特に文字エンコーディングが不明なソースから読み込む際に 便利です。例えばXMLドキュメントを読み込む際、最初の行のcharsetパラメータを チェックしてから適切な文字エンコーディング変換ポートを使うといった用途などです。 この動作はGauche独自の拡張です。

Function: read-string nchars :optional iport

[R7RS base] nchars文字を読み込み、もしくはそれ以前にEOFに達したら読めただけの文字を 使って文字列を作って返します。もし入力が既にEOFに達していた場合は EOFを返します。

Function: consume-trailing-whitespaces :optional iport

iportから、連続する空白文字を、最初の行末文字を読むまで読み込んで捨てます。 iportが省略された場合は現在の入力ポートが使われます。

これは主にラインバッファリングがある入力でインタラクティブなREPLを走らせる ためのものです。入力をreadで読み込んでいるとしましょう。 入力がラインバッファされていたら、ユーザがRETをタイプしてはじめてreadが 入力を読むことになります。ということは、ユーザが(read-line)RETとタイプしたなら、 read(read-line)を返し、もしそれを即座にevalするなら、 それは入力に残っているRETを読み込むことになるでしょう。

これはユーザが通常期待する動作とは異なります。 ユーザは、read-lineが実行された時点で、入力にタイプした行を読んで欲しいと 思うでしょう。したがって、ユーザの最初の入力のあと、バフぁFに残っているRETを含めた 空白文字を捨てておくことが役に立ちます。

Function: read-block nbytes :optional iport

この手続きは非推奨になりました。read-uvectorを使ってください (ユニフォームベクタのブロック入出力参照)。

nbytesバイトのデータをiportから読み込み、 不完全な文字列として返します。iportに十分なデータが無い場合、 返される文字列はnbytesより短いかもしれません。 nbytesが0の場合は、常に空文字列が返されます。

iportが既にEOFに達していた場合はEOFオブジェクトが返されます。

iportがファイルポートだった場合、read-blockは ポートのバッファリングモードによってふるまいが異なります (バッファリングモードの詳細についてはファイルポートを参照して下さい)。

  • バッファリングモードが:fullの場合、read-blockは EOFを読まない限り、nbytesのデータが読まれるまで待ちます。
  • バッファリングモードが:modest:noneである場合、 read-blockはEOFが読まれなくても、すぐに読めるデータがnbytesよりも 少ない場合はそのデータだけを返します。

データブロックをポートに書き出すには、データが文字列で表現されている 場合は単純にdisplayが使えます。データがuniform vectorで表現されている 場合はgauche.uvectorモジュールのwrite-uvectorが 使えます (ユニフォームベクタのブロック入出力参照)。

Function: eof-object

[R7RS base] EOFオブジェクトを返します。

Function: eof-object? obj

[R7RS base] objがEOFオブジェクトなら#tを返します。

Function: char-ready? :optional port

[R7RS base] portから文字が読み出せる状態ならば#tを返します。

今のところ、この手続きはportから少なくとも1バイト読み出せる状態なら#t を返します。そのバイトがマルチバイト文字を構成する場合、char-ready?を返した ポートから文字全てを読み込もうとすると、ブロックする可能性があります。 (通常の使用状況ではそのようなことは起きないでしょうが、理論的には起こり得ます。 慎重を期したい場合はread-uvectorでバイトシーケンスとして読み込んだ後、 入力文字列ポート等を使って文字毎に読むようにして下さい。)

Function: byte-ready? :optional port
Function: u8-ready? :optional port

[R7RS base] portから1バイトをすぐに読み込める状態なら#tを返します。

これは伝統的にbyte-ready?と呼ばれてきましたが、 R7RSではu8-ready?と名づけています。どちらも使えます。


6.21.7.2 リーダー字句モード

Parameter: reader-lexical-mode

現在のリーダ字句モードを表すパラメータです。 このパラメータの値を変えることにより、旧来のGaucheとR7RSで非互換となる コーナーケースの字句構文についての解釈を変更できます。

一般的には、このパラメータを直接変える必要のある場合は少ないでしょう。 字句構文は読み込み時に問題となりますが、パラメータの変更は実行時に起こります。 いつ、何が起きるかを正確に把握しているのでない限り、意図したとおりの効果は 得られないかもしれません。

hash-bang指示子 #!gauche-legacy および #!r7rs は 間接的にこのパラメータに影響を与えます。#!gauche-legacyは リーダモードをlegacyに、#!r7rsstrict-r7に 変更します。

コマンドライン引数の-fwarn-legacyはデフォルトのリーダモードを warn-legacyにセットします。

load中にこのパラメータを変更した場合、その効果はloadの期間中に 留まります。そのloadが終了した時点で、 load開始時のリーダモードが復元されます。

このパラメータは値として次のシンボルのうちいずれかを取ります。

permissive

これがデフォルトのモードです。二つの構文の、ほどほどの妥協点を見つけようとします。

文字列リテラル中の16進数エスケープシーケンスは、まず R7RSの字句構文で解釈されます。もしそれがR7RSの16進数エスケープシーケンスの 構文に沿っていなかった場合は、旧来のGaucheの構文で解釈されます。 例えば、"\x30;a" は、セミコロンがR7RSのエスケープシーケンスの終端と みなされ、"0a" と読まれます。一方で "\x30a""0a" と 読まれます。終端のセミコロンが無く、有効なR7RSの構文でないため、 旧来のGaucheのエスケープシーケンスとして2桁固定で解釈されるからです。 このモードでは、新たなコードでR7RSエスケープシーケンスを利用できるうえ、 ほとんどの既存のGaucheコードはそのまま走ります。 しかし、もし既存のGaucheコードで、たまたま2桁の16進数エスケープの直後に セミコロンがあった場合、それはR7RSとして読まれ、非互換性が生じます。

strict-r7

厳密にR7RS互換なモード。 リーダが、hash-bang指示子#!r7rsに出会うと、ファイルの残りの部分は このモードで読まれます。

このモードでは、Gaucheの拡張字句構文はエラーとなります。

他のR7RS実装でも読めるコードであることを確認したい場合に使ってください。

legacy

リーダは以前の(0.9.3.3及びそれ以前の)Gaucheと同じように動作します。 リーダがhash-bang指示子#!gauche-legacyに出会うと、ファイルの残りの部分は このモードで読まれます。

このモードが必要になるのは、2桁の16進数エスケープの直後にセミコロンが来ているリテラルを、 文字+セミコロンと読みたい場合だけです。例えば "\x30;a""0a"でなく"0;a"と読みたい、等。 こういうケースはコードではほとんど無いと思いますが、 データを文字列リテラルとしてダンプしてある場合、それも不完全な文字列として ダンプしてある場合に、こういった並びが出現しているかもしれません。

warn-legacy

リーダはpermissiveモードと同様に動作しますが、 旧来の16進数エスケープに出会うと警告を発します。 gosh-fwarn-legacyコマンドライン引数が与えられた場合は これがデフォルトのモードになります。

コードの中に非互換なエスケープシーケンスが無いかどうか調べるのに役立ちます。


6.21.7.3 読み込み時コンストラクタ

SRFI-10で定義されている読み込み時コンストラクタは、ユーザ定義の構造の 外部表現を作るための簡単な方法を提供します。

Reader Syntax: #,(tag arg …)

[SRFI-10] Gaucheはtag (シンボル)をコンストラクタ手続きに関連付ける グローバルなテーブルを管理しています。

リーダーがこの構文に出会ったとき、arg …を読み込み、 tagに関連付けられた読み込みコンストラクタを探し、 arg …を引数としてそのコンストラクタを呼び出し、 その構文を読み込んだ結果としてそのコンストラクタが返した値を挿入します。

この構文はリーダー内部で処理されることに注意して下さい。評価器は argを見ず、リーダーが返したオブジェクトしか見ません。

Function: define-reader-ctor tag procedure

[SRFI-10] 読み込みコンストラクタproceduretagに関連付けます。

例:

(define-reader-ctor 'pi (lambda () (* (atan 1) 4)))

#,(pi) ⇒ 3.141592653589793

'(#,(pi)) ⇒ (3.141592653589793)

(define-reader-ctor 'hash
  (lambda (type . pairs)
    (let ((tab (make-hash-table type)))
      (for-each (lambda (pair)
                  (hash-table-put! tab (car pair) (cdr pair)))
                pairs)
      tab)))

(define table
 #,(hash eq? (foo . bar) (duh . dah) (bum . bom)))

table ⇒ #<hash-table eq? 0x80f9398>
(hash-table-get table 'duh) ⇒ dah

write-objectメソッド(出力参照)と組み合わせて、 読み戻ることが可能なフォームで書かれたユーザ定義のクラスを 作ることが簡単になります。

(define-class <point> ()
  ((x :init-value 0 :init-keyword :x)
   (y :init-value 0 :init-keyword :y)))

(define-method write-object ((p <point>) out)
  (format out "#,(<point> ~s ~s)" (ref p 'x) (ref p 'y)))

(define-reader-ctor '<point>
  (lambda (x y) (make <point> :x x :y y)))

注意: define-reader-ctorの効果の範囲はSRFI-10には 規定されておらず、SRFI-10をサポートする実装においても互換性の問題を 起こすことがあるかもしれません。 (実際に、define-reader-ctorの存在そのものが実装の選択に 任されています。)

Gaucheでは、現時点においては、define-reader-ctorはそのフォームが コンパイルされ評価された時点で効力を持ちます。 Gaucheはトップレベルのフォームを順番にコンパイル・評価するので、 define-reader-ctorで指定されたtagは、その指定の直後から 使えます。 しかし、define-reader-ctorの呼び出しとtagの使用が beginフォームで囲まれている場合は、beginフォーム全体は 評価される前に一度にコンパイルされるため、うまく動作しません。

他の実装では、define-reader-ctorの呼び出しが効力を持つようにする 前にファイル全体を読み込むことを要求するかも知れません。 その場合は、define-reader-ctorと定義されたtagの使用を 同じファイルに置く事は実質的に不可能です。 可能ならば、define-reader-ctorの呼び出しと、tagの使用は 異なるファイルに分離されることが望まれます。

現在のdefine-reader-ctorに関するもう1つの問題は、それが Gaucheシステムのグローバルテーブルを変更してしまうことで、それゆえに モジュール性が良くありません。 複数人によって書かれたコードは同じタグを使っているかも知れず、 期待されない結果を引き起こすかも知れません。 作者にはまだ明確なアイデアがありませんが、将来のバージョンでは、 Gaucheにはtagのスコープをカプセル化する方法が導入されるかも しれません。


6.21.7.4 入力ユーティリティ手続き

Function: port->string port
Function: port->list reader port
Function: port->string-list port
Function: port->sexp-list port

便利な入力手続きです。APIはScshとSTkから取りました。

port->stringportをEOFまで読み込み、 読んだものを文字列として返します。

port->listは手続きreaderportに繰り返し適用し、 結果をリストに蓄積します。readerがEOFを返したら 蓄積されたリストを返します。 port自体はクローズされないことに注意してください。

port->string-listport->listread-lineで特定化したもので、 port->sexp-listport->listreadで特定化したものです。

もし入力にGaucheの内部エンコーディングにおいて有効な文字をつくらない バイト列が含まれていた場合、port->stringport->string-list の返り値には不完全な文字列が含まれている可能性があります。 入力をバイナリデータとして扱いたい場合は、 gauche.uvectorport->uvectorを使って下さい (ユニフォームベクタのブロック入出力参照)。

Function: port-fold fn knil reader
Function: port-fold-right fn knil reader
Function: port-for-each fn reader
Function: port-map fn reader

readerによって読まれる入力に対する便利な繰り返し手続きです。

実際にはこれらの手続きはポートからの入力以外にも使えるため、それぞれ generator-fold, generator-fold-right, generator-for-each, generator-mapによって置き換えられました。 詳しくは生成された値の畳み込みを参照してください。

これらの手続きは後方互換性のためだけに提供されています。


Previous: , Up: 入出力   [Contents][Index]

6.21.8 出力


Next: , Previous: , Up: 出力   [Contents][Index]

6.21.8.1 出力ルーチンの階層

Gaucheにはたくさんの出力手続きがあり、慣れないうちはどれを使えばよいか迷ってしまう かもしれません。次の表は様々な出力手続きを分類するものです。

オブジェクトの出力

Schemeオブジェクトを出力する手続きです。 もっと低レベルの手続きもありますが、一般にはこの層が出力ルーチンの基本であると みなされています。Schemeのオブジェクト単位で操作をするからです。 この手続き群はさらに次の二つのグループに分かれます。

  • Write族手続き: write, write-shared, write-simple—これらは、Schemeオブジェクトの外部表現を出力します。出力されたものは一般に、 readを使って、可能な限り情報を失わずに読み戻すことができます1。 Schemeオブジェクトの外部表現は、リテラルデータをプログラム中に記述するのと 同じ書き方なので、これがSchemeオブジェクトを出力する際のデフォルトの表現になっています。
  • Display族手続き: display, print, newline. これらは人間に読みやすいプレーンテキストな出力を生成します。
高レベル整形出力

出力を指定の桁数に揃えたり、どちらかに寄せたり、といった書式を指定できる手続きが formatです。Cのprintfのようなものです。

低レベル、特定の型用の出力

「生」のデータを扱う手続きです。

  • 文字毎やバイト毎の出力: write-char, write-byte
  • 文字列やバイナリデータの配列の出力: write-string, write-uvector
  • 出力バッファのフラッシュ: flush, flush-all-ports

6.21.8.2 出力制御

Class: <write-controls>

Lisp構造の出力形式は、 <write-controls>オブジェクトによって制御することができます。 Lispオブジェクトを出力するルーチン(write, display等)や、 高レベル出力手続き(format等)は出力制御オブジェクトを取ることができます。

以下の例で、出力制御の様子がわかると思います。

(write '(1 10 100 1000)
       (make-write-controls :base 16 :radix #t))
 prints (#x1 #xa #x64 #x3e8)

(write (iota 100)
       (make-write-controls :length 5))
 prints (0 1 2 3 4 ...)

make-write-controls手続きは出力制御オブジェクトを作って返します。 出力制御オブジェクトは以下のスロットを持ちます (これらのスロット名はCommon Lispの対応するプリント制御変数から取られました)。

Instance Variable of <write-controls>: length

このスロットが非負整数の場合、それはリストとベクタ(ユニフォームベクタを含む)の 要素が表示される最大数を指定します。もしリストやベクタがその最大数より 多くの要素を持っている場合、残りの要素は表示されず、代わりに エリプシスが表示されます。このスロットが#f(デフォルト)であれば、 すべての要素が表示されます。

Instance Variable of <write-controls>: level

このスロットが非負整数の場合、それは構造体(リストとベクタ)が表示される 最大の深さを指定します。もし構造体がより深いノードを持っている場合、 そのノードの代わりに#が表示されます。 このスロットが#f(デフォルト)であれば、すべての要素が表示されます。

Instance Variable of <write-controls>: base

このスロットは2以上36以下の整数でなければならず、 正確な整数を印字する基数を指定します。デフォルトは10です。

Instance Variable of <write-controls>: radix

このスロットは真偽値を持ちます。真であれば、 正確な整数の前に常に基数プレフィクスが表示されます。 デフォルトは#fです。

Instance Variable of <write-controls>: pretty

このスロットが真の値の場合、 指定されたカラム幅内で、改行やインデントを適宜挿入してネストした構造を見やすく表示する、 プリティプリント機能を使います。

Instance Variable of <write-controls>: width

このスロットが非負整数の場合、プリティプリントで使われる表示幅を指定します。

Instance Variable of <write-controls>: indent

このスロットの値は非負の正確な整数でなければなりません。 このスロットの値がゼロより大きく、prettyスロットが偽でなければ、 プリティプリンタは改行文字を出力した後にこのスロットの数だけ空白文字を出力します。 つまり、2行め以降がインデントされることになります。 prettyスロットが偽であればこのスロットは無視されインデントも行われません。

最初の行はインデントされないことに注意してください。 想定する使い方は、あるカラムからプリティプリントを開始することです。この場合、 1行目はそのまま出力できますが、2行目以降を1行目と揃うようにインデントする必要があります。

Instance Variable of <write-controls>: string-length

このスロットは非負整数か#fです。非負整数の場合、その長さ以上の文字列リテラルは 切り詰めて表示されます。このスロットの値ぶんの文字が出力されたのち、 エリプシスとダブルクオートが出力されます。

出力制御オブジェクトは変更不可です。もし既に存在する制御オブジェクトを 少し変えたものが欲しければ、write-controls-copyが使えます。

註: 0.9.5で<write-controls>オブジェクトを導入した際には、 Common Lispのスペシャル変数の名前をそのまま反映した print-lengthprint-pretty等をスロット名としていました。 しかし出力制御オブジェクトの一部であることがわかっている以上、print-の部分は冗長です。 そこで0.9.6からスロット名を変えました。 互換性のために、make-write-controlswrite-controls-copyは 古いスロット名でのキーワード引数も認識します。 スロットを直接指定してアクセスしているコードは修正の必要があります。 1.0リリース時に古いスロット名のサポートを落とす予定です。

Function: make-write-controls :key length level base radix pretty width indent string-length

出力制御オブジェクトを作って返します。

Function: write-controls-copy controls :key length level base radix pretty width indent string-length

出力制御オブジェクトcontrolsのコピーを返します。もしキーワード引数が 与えられれば、その要素が置き換えられます。

高レベル出力手続きはwrite-objectメソッドを経由して再帰的に 呼ばれる可能性があります。その場合、大元の出力手続き呼び出しに渡された 出力制御オブジェクトが、同じポートに対して出力する再帰的に呼ばれる 出力手続きへと自動的に引き継がれます。


6.21.8.3 オブジェクトの出力

以下の手続きで、省略可能な引数portは出力ポートでなければなりません。 省略された場合が現在の出力ポートが使われます。

いくつかの手続きはport/controls引数を取ります。これは、出力ポート もしくは<write-controls>オブジェクトでなければなりません。例えば writeは二つまでそういった引数を取ります。すなわち、writeは 以下のいずれの形式でも呼び出すことができます: (write obj)(write obj port)(write obj controls)(write obj port controls)(write obj controls port)。 省略された場合、ポートは現在の出力ポート、出力制御についてはデフォルトの出力制御が 使われます。

Function: write obj :optional port/controls1 port/controls2
Function: write-shared obj :optional port/controls1 port/controls2
Function: write-simple obj :optional port/controls1 port/controls2

[R7RS+ write] write族の手続きは、Schemeオブジェクトの外部表現を出力します。 外部表現は一般に、read手続きによって読み戻せば、 元のオブジェクトと同等のオブジェクトとなります。 この3つの手続きの違いは、共有構造および循環構造の扱いにあります。

writeは循環構造に対して安全です。データに循環がある場合は データラベル記法(#n=#n#)を用いてそれを表現します。 循環の無い、単に共有されているだけの構造についてはデータラベル記法を使いません (二番目の例参照)。

(let1 x (list 1)
  (set-cdr! x x)   ; create a cycle
  (write x))
 ⇒ shows #0=(1 . #0#)

(let1 x (list 1)
  (write (list x x)))
 ⇒ shows ((1) (1))

write-sharedも循環構造に対して安全で、さらに共有構造も データラベル記法で表示します。グラフ構造のトポロジーを維持する必要がある 場合はこの手続きを使ってください。

(let1 x (list 1)
  (write-shared (list x x)))
 ⇒ shows (#0=(1) #0#)

最後に、write-simpleはオブジェクトを、共有構造や循環構造を考慮せずに 再帰的に表示します。これは出力前に共有構造の検出パスを走らせる必要がないので 高速です。しかし、渡されたデータに循環があった場合は出力が止まらなくなります。

これらの手続きが表示中にユーザ定義クラスのオブジェクトに 出会った場合は、ジェネリックファンクションwrite-objectを呼び出します。

歴史的経緯: writeは以前のScheme標準にもありましたが、 循環構造があった場合の振る舞いはR7RSになるまで規定されていませんでした。 実際、Gauche 0.9.4より前のwriteは、循環構造を渡されると停止しません。 SRFI-38はデータラベル記法および、それを表示するための write-with-shared-structurewrite/ss手続きを導入し、 Gaucheもそれをサポートしていました。 R7RSでこの問題は明確にされ、Gaucheも0.9.4からそれに沿っています。

Function: write-with-shared-structure obj :optional port
Function: write/ss obj :optional port
Function: write* obj :optional port

[SRFI-38] これらは上記のwrite-sharedの別名です。

Gaucheは、STklosから取ったwrite*という名前を長く使ってきましたが、 SRFI-38によってwrite-with-shared-structurewrite/ssが 定義されました。これらの名前は互換性のためだけに残してあります。 新たなコードにはwrite-sharedを使ってください。

Function: display obj :optional port/controls1 port/controls2

[R7RS write] オブジェクトobjの人に読みやすい表現を、出力ポートに書き出します。

objが循環構造を含んでいる場合は、データラベル記法を使って出力します。

displayが表示中にユーザ定義クラスのオブジェクトに 出会った場合は、ジェネリックファンクションwrite-objectを呼び出します。

(display "\"Mahalo\", he said.")
 ⇒ shows "Mahalo", he said.

(let ((x (list "imua")))
  (set-cdr! x x)
  (display x))
 ⇒ shows #0=(imua . #0#)
Function: print expr …

expr … をdisplayを使って現在の出力ポートに表示し、 最後に改行を書き出します。

Function: pprint obj :key port controls width length level newline indent

objportに整形出力(prettyprint)します。 portが省略された場合は現在の出力ポートが使われます。 この手続きと同じ効果は、write手続きにprettyスロットを#tにした 出力制御オブジェクトを渡すことでも得られます (実際、内部的にはpprintはそうやってwriteを呼び出しているだけです) が、手軽に整形出力を試してみたい場合にこの手続きは便利です。

デフォルトでは、pprintobjを出力した後に改行します。 この動作を抑制するにはnewline#fを渡してください。

出力の整形をカスタマイズするには、controlsキーワード引数に出力制御オブジェクトを 渡します(渡された制御オブジェクトのprettyスロットは無視されます; 常に出力は整形されます)。さらに、出力制御オブジェクトの widthlengthlevelindentスロットを、 それぞれのキーワード引数でオーバライドできます。 controlsを省略した場合は適切なデフォルト値が使われます。 出力制御の詳細については出力制御を参照してください。

(pprint (make-list 6 '(gauche droite)))
 ⇒ prints
  ((gauche droite) (gauche droite) (gauche droite) (gauche droite)
   (gauche droite) (gauche droite))

(pprint (make-list 6 '(gauche droite)) :width 20)
 ⇒ prints
  ((gauche droite)
   (gauche droite)
   (gauche droite)
   (gauche droite)
   (gauche droite)
   (gauche droite))

(pprint (make-list 6 '(gauche droite)) :length 3)
 ⇒ prints
  ((gauche droite) (gauche droite) (gauche droite) …)

(pprint (make-list 6 '(gauche droite)) :level 1)
 ⇒ prints
  (# # # # # #)
Method: write-object (obj <object>) port

このメソッドをつかって、オブジェクトをどのように印字するかをカスタマイズ できます。

Function: newline :optional port

[R7RS base] portに改行文字を書き出します。(write-char #\newline port)(display "\n" port)と書いても同じことですが、歴史的な理由から 残されています。

Function: fresh-line :optional port

出力カラムが行頭であれば何もせず#fを返します。そうでなければ#\newline を出力して#tを返します。 次の出力を行頭から始めたいけれど、余分な空行は出力したくない、という時に便利です。

この手続きはCommon Lispから取りました。

常にポートの出力カラムがわかるとは限りません。特に、テキスト出力とバイナリ出力を 混ぜた場合、出力カラムのトラッキングは信頼できないものとなります。 出力カラムが特定できない場合は、この手続きは改行文字を出力します。


6.21.8.4 フォーマット出力

Function: format [d/c1 [d/c2]] format-string arg …

[SRFI-28+] string の指示に従い、arg …をフォーマットします。 この手続きはCommonLispのformatのサブセットに、Gauche独自の拡張を 加えたものです。また、これはSRFI-28 "Basic format strings" のスーパーセットに なっています。

この手続きは奇妙なシグネチャを持っています。 最初の二つの省略可能引数、d/c1d/c2は、 出力先を指定するオブジェクトか、~s~aの出力形式をカスタマイズする <write-controls>オブジェクトです(出力制御参照)。 それらをどちらも渡さないことも、どちらかだけを渡すことも、両方渡すこともできます。 両方渡す場合の順番は自由です。

註: SRFI-28のformatformat-stringの前に省略可能引数を取りません。

出力先を指定するオブジェクトを渡す場合、それは出力ポートか真偽値でなければなりません。 それが出力ポートであれば、フォーマットされた 結果はそのポートに書き出されます。port#tであれば、結果は 現在の出力ポートに書き出されます。port#fであれば、結果は 文字列としてformatから返されます。 destは省略することもできます。その場合は、dest#f を指定したのと同じ動作をします(SRFI-28のformat)。

stringはフォーマット指示子を含んだ文字列です。 フォーマット指示子はチルダ‘~’から始まり、特定の文字で終了する文字の並びで、 それぞれのフォーマット指示子が対応するargを取りフォーマットします。 string内のフォーマット指示子以外の文字列はそのまま出力されます。

(format #f "the answer is ~s" 42)
  ⇒ "the answer is 42"

フォーマット指示子は一つ以上のコンマで区切られたパラメータを取ることもできます。 パラメータは整数か文字です。文字の場合、クオート文字に続けてその文字を置きます。 パラメータが省略された場合は既定値が使われます。パラメータの意味はフォーマット指示子毎に 異なります。

さらに、フォーマット指示子は2種類のフラグ、‘@’ と ‘:’ を 取ることができます。これらの組合せでフォーマットの動作が変わります。フラグは (もしあれば)パラメータの後、指示子の文字の直前に置かれなければなりません。

パラメータの位置に文字 ‘v’ か ‘V’ を置くこともできます。 その場合、パラメータの値が引数リストから取られます。対応する引数は整数か 文字、または#fでなければなりません。#fの場合はそのパラメータが 省略されたのと同じになります。

いくつかの例です。

~10,2s

パラメータ10と2を伴う、フォーマット指示子~s

~12,,,'*A

第1パラメータに数値12、第4パラメータに文字‘*’を取るフォーマット指示子~a。 第2と第3のパラメータは省略されています。

~10@d

フォーマット指示子~d。パラメータ10と‘@’フラグがついています。

~v,vx

フォーマット指示子~x。第1パラメータと第2パラメータは引数リストから取られます。

以下にサポートされているフォーマット指示子を示します。フォーマット指示子の文字自体は 大文字であっても小文字であっても構いません。特に断りのない限り両者は同じ動作をします。

~A

パラメータ: mincol,colinc,minpad,padchar,maxcol

ASCII出力。対応する引数がdisplayを使ってフォーマットされます。 整数がmincolに与えられた場合、それは出力される最小の文字数を指定します。 引数のフォーマット結果がmincolより短ければ、空白が右に追加されます(つまり、 左詰めになります)。

colincminpad、そしてpadcharは更に細かいパディング方法を 指定します。padcharに文字が与えられた場合、それが空白文字の代わりにパディング文字と して使われます。minpadに0以上の整数が与えられた場合、少なくともその数だけの パディング文字が追加されます。colincが指定された場合、 追加されるパディング文字の数がcolincの倍数に調整されます。

アトマーク ‘@’ フラグが与えられた場合、結果は右詰めになります。

maxcolパラメータは与えられていれば書かれる文字数の上限を指定します。 フォーマット後の文字列の長さがmaxcolを超えた場合、maxcol文字だけが 書かれます。コロン ‘:’ フラグが同時に与えられていれば、 maxcol - 4 文字が書かれた後、文字列“ ...”が書かれます。

(format #f "|~a|" "oops")
  ⇒ "|oops|"
(format #f "|~10a|" "oops")
  ⇒ "|oops      |"
(format #f "|~10@a|" "oops")
  ⇒ "|      oops|"
(format #f "|~10,,,'*@a|" "oops")
  ⇒ "|******oops|"
(format #f "|~10,,,'☆a|" "oops")
  ⇒ "|oops☆☆☆☆☆☆|"

(format #f "|~,,,,10a|" '(abc def ghi jkl))
  ⇒ "|(abc def gh|"
(format #f "|~,,,,10:a|" '(abc def ghi jkl))
  ⇒ "|(abc de ...|"
~S

パラメータ: mincol,colinc,minpad,padchar,maxcol

S式出力。対応する引数がwriteを使ってフォーマットされます。 パラメータの意味は~A指示子と同じです。

(format #f "|~s|" "oops")
  ⇒ "|\"oops\"|"
(format #f "|~10s|" "oops")
  ⇒ "|\"oops\"    |"
(format #f "|~10@s|" "oops")
  ⇒ "|    \"oops\"|"
(format #f "|~10,,,'*@s|" "oops")
  ⇒ "|****\"oops\"|"
(format #f "|~10,,,'★s|" "oops")
  ⇒ "|\"oops\"★★★★|"
~W

パラメータ: なし

きれいな出力。フラグが無ければ~Sと全く同じですが、 コロンフラグが与えられるとプリティプリントを行います。つまり、 <write-context>prettyスロットが一時的に真になります。 また、アトマークフラグが与えられると、S式の長さ制限および深さ制限を一時的にオフにします。 つまり、<write-context>lengthlevelスロットを#f にします。

プリティプリントのインデントは~Wの出力開始位置にセットされるので、 プリティプリントによって出力が複数行になった場合も開始位置に揃うようにインデントされます。

(format #t "Result: ~:w\n" (make-list 40 'aaaa))
 ⇒ prints:
Result: (aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
         aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
         aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa)
~C

パラメータ: なし

文字出力。対応する引数は文字でなければなりません(そうでなければエラーが報告されます)。 フラグが無ければ文字はdisplayで表示されます。 アトマークフラグが与えられた場合はwriteで表示されます。

~D

パラメータ: mincol,padchar,commachar,interval

10進出力。対応する引数が10進数表記でフォーマットされます。もし引数が数値でなければ、 全てのパラメータは(‘v’パラメータの処理後に)無視され、 引数は~Aでフォーマットされます。

もしmincolに整数が与えられたら、それが最小の文字数を指定します。 結果の文字数がそれより少なければ、文字padcharが左に追加されます(右詰めになります)。 padcharが省略された場合は空白文字が使われます。

(format #f "|~d|" 12345)
  ⇒ "|12345|"
(format #f "|~10d|" 12345)
  ⇒ "|     12345|"
(format #f "|~10,'0d|" 12345)
  ⇒ "|0000012345|"

アトマーク ‘@’ フラグが与えられた場合、正の引数に対して ‘+’ が 先頭につけられます。

コロンフラグ ‘:’ が与えられた場合、結果の文字はinterval文字毎に まとめられ、間に文字commacharが挿入されます。デフォルトでは3文字毎にコンマが 挿入されます。

(format #f "|~:d|" 12345)
  ⇒ "|12,345|"
(format #f "|~,,'_,4:d|" -12345678)
  ⇒ "|-1234_5678|"
~B

パラメータ: mincol,padchar,commachar,interval

2進出力。対応する引数が2進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~O

パラメータ: mincol,padchar,commachar,interval

8進出力。対応する引数が8進数の整数としてフォーマットされます。 パラメータの意味は~Dと同じです。

~X
~x

パラメータ: mincol,padchar,commachar,interval

16進出力。対応する引数が16進数の整数としてフォーマットされます。 フォーマット指示文字に ‘X’ が与えられた場合は ‘ABCDEF’ が桁文字として 使われ、 ‘x’ が与えられた場合は ‘abcdef’ が桁文字として使われます。 パラメータの意味は~Dと同じです。

(format #f "~8,'0x" 259847592)
  ⇒ "0f7cf5a8"
(format #f "~8,'0X" 259847592)
  ⇒ "0F7CF5A8"
~R
~r

パラメータ: radix,mincol,padchar,commachar,interval

基数(Radix)。引数が正確数であれば、 ~nRは引数を基数nの数値としてフォーマットします。 例えば~10R~Dと同じです。 radixパラメータは2以上36以下でなければなりません。 他のパラメータの意味は~Dと同じです。

引数が正確数以外の場合は、~Aのようにフォーマットします。

大文字Rが使われた場合、11進数以上では大文字アルファベットが使われます。 小文字rの場合は小文字アルファベットが使われます。

アトマークフラグが与えられた場合は違った動作になります。 引数は正の正確な整数でなければならず、それがローマ数字で表示されます (例えば2023ならMMXXIIIとなります)。 アトマークに加えてコロンフラグも与えられると、4をIVのかわりにIIII と表示する変化形を使います。 ローマ数字の5,000はASCIIでは表せないので、Common Lispはこの機能は 4,999までしかサポートしていません。が、Unicodeにはローマ数字が100,000まで あるので、Gaucheでは499,999までをサポートします。 Rrの区別は出力の大文字小文字に反映されます。

radixもアトマークフラグも与えられない場合、 Common Lispでは引数の数字を英語表記で出力します。 この機能はまだ実装されておらず、エラーが投げられます。

(format #f "~36R" 123456789) ⇒ "21I3V9"
(format #f "~36r" 123456789) ⇒ "21i3v9"
(format #f "~@R" 1999)  ⇒ "MCMXCIX"
(format #f "~@:R" 1999) ⇒ "MDCCCCLXXXXVIIII"
(format #f "~@r" 1999)  ⇒ "mcmxcix"
(format #f "~@R" 123456) ⇒ "ↈↂↂMMMCDLVI"
~F

パラメータ: width,digis,scale,ovfchar,padchar

浮動小数点数出力。 引数が実数ならば、その十進小数表現が出力されます。 widthパラメータは出力幅を指定します。もしフォーマット結果がその幅に足りなければ、 左側にpadcharが詰められます。padcharのデフォルトは#\spaceです。 フォーマット結果がwidthを越える場合、ovfcharが指定されていれば、 width個のovfcharが出力され、指定されていなければwidthを無視して 必要なだけの桁数が出力されます。

(format "~6f" 3.14)          ⇒ "  3.14"
(format "~6f" 3.141592)      ⇒ "3.141592"
(format "~6,,,'#f" 3.141592) ⇒ "######"
(format "~6,,,,'*f" 3.14)    ⇒ "**3.14"

digitsパラメータは小数点以下に表示される桁数を指定します。非負整数でなければなりません。 省略された場合は、引数の浮動小数点数を一意に同定できる最小の精度で出力されます (writedisplayで出力した場合と同じです。その文字列をreadすれば、 全く同じ浮動小数点数が得られます)。

(format "~6,3f" 3.141592)    ⇒ " 3.142"
(format "~6,0f" 3.141592)    ⇒ "    3."
(format "~10,4f" 355/113)    ⇒ "    3.1416"
(format "~10,4f" 3)          ⇒ "    3.0000"

scaleパラメータが与えられた場合、引数が表示前に(expt 10 scale)倍 されます。

@フラグが与えられると、引数が非負の場合にプラス記号が出力されます。

(format "~8,3@f" 3.141592)   ⇒ "  +3.142"

digitsが浮動小数点数を一意に同定するための桁数に足りない場合は、 小数点以下digits桁に丸められます。デフォルトでは、この丸めは 浮動小数点数の真の値に基づいて行われます。つまり、真の値に近い方の表現が選ばれます。 これはたまに、非直感的な結果になることがあります。 例えば1.15を1/100の位で丸めて(1/10単位にして)みると、 小学校でやる算数と異なり、1.1が出力されます。 この理由は、1.15と表記される浮動小数点数が実際には1.15よりわずかに小さく、従って 1.1により近いからです。本当は1.15でないものを1.15と表示しているのは、 1.15により近い浮動小数点数が他に無く、1.15が該当する浮動小数点数を一意に同定する 最も短い表現だからです。

しかし日常的なアプリケーションでこういう振る舞いがあると、ユーザが混乱してしまうかもしれません。 そこでもう一つ、表示に基づいた丸め(notational rounding)モードを用意しました。 このモードでは、浮動小数点数の表示に基づいた丸めを行います。従って 1.15を1/100の位で丸めると1.2になります。このモードは:フラグで指定できます。

(format "~6,1f" 1.15)  ⇒ "   1.1"
(format "~6,1:f" 1.15) ⇒ "   1.2"
~$

パラメータ: digits,idigits,width,padchar

通貨の表示に適した浮動小数点数フォーマット。 digitsは小数点以下に表示される桁数で、デフォルトは2です。 idigitsは小数点より前に表示される最小の桁数で、デフォルトは1です。 widthパラメータは全体の最少文字数を指定し、表示すべき文字がそれより少ない場合は padcharが左側に足されます。widthのデフォルトは0、 padcharのデフォルトは#\spaceです。

@フラグが与えられた場合、非負の値に+符号が表示されます。

:フラグが与えられた場合、符号があればそれはパディングより前に表示されます。

引数が実数でない場合は、あたかも~wD (wwidth) が指定 されたかのように表示されます。

Gauche特有の機能として、数値は表示に基づいた丸め(notational rounding)で 丸められます。表示に基づいた丸めについては上の~Fの項の説明を参照してください。

(map (cut format "~$" <>) '(1.23 4.5 6))
  ⇒ '("1.23" "4.50" "6.00")
~?

パラメータ: なし

再帰的フォーマッティング。この指示子はまず文字列を引数に取り、それをフォーマット文字列と 解釈します。そしてさらに次の引数(それはリストでなければなりません)を、そのフォーマット文字列 への引数と解釈します。

(format "~s~?~s" '< "~s ~s" '(a b) '>)
  ⇒ "<a b>"

@フラグが与えられた場合は、最初の引数をフォーマット文字列と解釈した後で、 続く引数を必要なだけそのフォーマット文字列の引数として使います。

(format "~s~@?~s" '< "~s ~s" 'a 'b '>)
  ⇒ "<a b>"
~*

パラメータ: count

引数のカウンタをcountだけ後方にずらします。つまり、count個の引数が 無視されることになります。countのデフォルト値は1です。 コロンフラグが与えられた場合は引数カウンタを前方に動かします。 例えば~:*は次のディレクティブが直前に使った引数を再び使うようにします。 アトマークフラグが与えられた場合は、countが引数の絶対位置を示します。 0が最初の引数です。

~P

パラメータ: なし

複数形(plural)。引数が正確な1であれば何もせず、そうでなければsを出力します。

アトマークフラグが与えられた場合は、引数が正確な1ならyを、 そうでなければiesを出力します。

コロンフラグが与えられた場合は、引数をひとつ戻ってから処理します。

註: 1.0は複数とみなされます。

(format "~a book~:p, ~a book~:p, ~a pon~:@p, ~a pon~:@p" 1 2 1 2)
 ⇒ "1 book, 2 books, 1 pony, 2 ponies"
~&

パラメータ: count

出力位置が行の頭なら何もせず、そうでなければ#\newline文字を出力します。 fresh-line手続きと同じです (オブジェクトの出力参照)。

もしcountが与えられて1より大きければ、fresh-lineの後に (- count 1)個の#\newlineを出力します。 countが0なら何もしません。

~~
~%
~t
~|

パラメータ: count

それぞれ、文字#\~#\newline#\tab#\pageを ひとつ出力します。countが与えられた場合はその個数出力します。

~ + newline

パラメータ: なし

改行の無視。Schemeはリテラル文字列の中でバックスラッシュ+改行のエスケープで 無視される改行を入れられるのでこれは必要ないのですが、Common Lispとの互換性のために サポートされています。 フラグが無ければ、チルドに続く改行文字と、そのあとに続く空白文字(char-whitespace?) を無視します。コロンフラグが与えられたら、チルドの直後の改行文字だけ無視します。 アトマークフラグが与えられたら、 チルド直後の改行文字は残しますが、その後に続く空白文字は無視します。

(format "abc~\n   def") ⇒ "abcdef"
(format "abc~:\n   def") ⇒ "abc   def"
(format "abc~@\n   def") ⇒ "abc\ndef"

(format "abc~:\n   \ndef") ⇒ "abc   \ndef"
~[

パラメータ: なし

条件分岐。以下の3つの形をとります。

~[fmt0~;fmt1~;...~]

引数をひとつ消費します。それが正確な整数でなければエラーが投げられます。 それが0ならfmt0が、1ならfmt0が、…という具合に選択されます。 該当するフォーマット文字列が無ければ空のフォーマット文字列として振る舞います。 ただし、最後の区切りを~;ではなく~:;にすることで、 それに続くフォーマット文字列を「デフォルト」のフォーマット文字列とすることができます。

~:[fmt-alternative~;fmt-consequence~]

引数をひとつ消費します。それが偽ならfmt-alternativeが、 真ならfmt-consequenceが選択されます (ifと順序が逆であることに注意してください。偽を0、真を1と考えると良いでしょう)。

~@[fmt-consequence~]

引数を消費せずに調べます。それが偽なら、その引数を消費した上で空のフォーマット文字列として 振る舞います。それが真なら、fmt-consequenceを選択します。 従って、fmt-consequenceは通常、引数をひとつ消費するフォーマット指示子を含みます。

(map (cut format "~[zero~;one~;two~]" <>) '(0 1 2 3))
  ⇒ ("zero" "one" "two" "")
(map (cut format "~[zero~;one~;two~:;other~]" <>) '(0 1 2 3 -1))
  ⇒ ("zero" "one" "two" "other" "other")
(map (cut format "~:[no~;yes~]" <>) '(#f #t))
  ⇒ ("no" "yes")
(map (cut format "~@[I got ~a~]" <>) '(#f rhythm))
  ⇒ ("" "I got rhythm")

6.21.8.5 低レベル出力

Function: write-char char :optional port

[R7RS base] 文字charをポートに出力します。

Function: write-byte byte :optional port
Function: write-u8 byte :optional port

[R7RS base] 出力ポートに1バイトのデータbyteを書き出します。 byteは0から255の間の正確な整数でなければなりません。

これは伝統的にwrite-byteと呼ばれてきましたが、 R7RSではwrite-u8と名づけています。どちらも使えます。

Function: write-string string :optional oport start end

[R7RS base] startend引数が省略された場合は(display string oport)と 同じです。それらが与えられた場合は、string中の指定範囲のみが出力されます。

Function: flush :optional port
Function: flush-all-ports

それぞれ、port、および全てのポートにバッファされているデータを 全て書き出します。

R7RSではflushflush-output-portと呼ばれています。 Gaucheではscheme.baseモジュールでflushの別名として 提供しています(scheme.base - R7RS基本ライブラリ参照)。

手続き"flush"はScheme実装によって様々な名前で呼ばれています: force-output (Scsh, SCM)、 flush-output (Gambit)、flush-output-port (Bigloo) 等。 flushの名前はSTkとSTklosから取りました。


Footnotes

(1)

ある意味、 これは他のプログラミング言語でシリアライズやマーシャライズと呼ばれている操作に 似ています。一般のSchemeオブジェクトをディスクやネットワークにwriteして、 それをreadすれば、元のオブジェクトと等価なオブジェクトが得られるわけです。 Lisp族言語では、これはread/write invarianceと呼ばれ、組み込みの機能と なっています。但しオブジェクトによってはこの性質を持たないものもあり、 そういうオブジェクトを扱う時は専用のシリアライザを書く必要があります。


Next: , Previous: , Up: 組み込みライブラリ   [Contents][Index]


For Gauche 0.9.15Search (procedure/syntax/module):