ストリーム (プログラミング)

ストリームとは、データを、比較的小さい単位が連続したものと捉え、上流から下流へ「流れるもの」とみなし、そのデータの入出力・送受信を最小限の滞留とさせ低遅延処理となるように扱う形態を指す。またその操作のための抽象データ型を指す。

処理内部では適切なデータ分割・バッファリングが行われる。

入力ストリーム  を利用してデータの読み出しを行ない、出力ストリーム  を利用してデータの書き込みを行なう。対照的な概念としては、保管・永続化されたデータ形態がある。

メモリバッファの入出力を扱うもの、ネットワーク通信を扱うものなどさまざまなものがある。ファイルの入出力に対しては、ストリームとの変換を行う仕組みが用意される。

典型例として、Unixオペレーティングシステムおよびそのシェルによって実装されたものがある。

1. 標準ストリーム
----------

オペレーティングシステムの標準的な入力元や出力先に対して、標準ストリームと呼ばれる特別なストリームが用意される。

キーボードからの入力、ディスプレイへの出力も、標準ストリームによって抽象化され、個別のプログラムからはデバイスハードウェアを意識する必要がなくなる。

2. C言語
------

C言語ではUnix由来の標準ストリームとして、標準入力stdin、標準出力stdout、標準エラー出力stderrが定義されている。これらはすべてファイルストリームとして抽象化されており、ファイルポインタFILE*のひとつとして扱うことができる。標準Cライブラリのヘッダーファイルにおいて、ストリームを扱う関数が定義されている。例えばprintf関数は標準出力への書き込みを、scanf関数は標準入力からの読み出しを行なう。

3. C++
------

3.1. 入出力ストリーム
-------------

C++では、標準C++ライブラリのヘッダーファイルにおいて、標準入出力ストリームとしてcin、cout、cerr、clogが定義されている。ワイド文字列用には別途wcin、wcout、wcerr、wclogが定義されている。

include int main { std::cout << "Hello, World!" << std::endl; return 0; }<WtXmlEndTag />

C++は強く型付けされた言語であるが、この標準入出力ストリームは多重定義によって全く型を意識せずに入出力を行なえる。特定の演算子を多重定義したユーザー定義型も利用できるため、非常に柔軟な入出力が可能である。さらに入出力操作子の導入により、細かな制御が可能となった。

include
include ... int i; std::string s; my_data_type d; std::cin >> i; // 整数の入力をiに読み込む。 std::cin >> std::setw >> s; // 10文字読み込む。 std::cin >> d; // オーバーロードすることで任意のデータをdに読み込む。<WtXmlEndTag />

これらの機能により、従来の型消去  や型昇格  を伴う可変長引数を利用したprintf関数で起こりがちであった、型の不一致や制御子の過不足によるバグの可能性がきわめて低くなった。しかしその半面、マニピュレータの仕様など覚えなければならないことが増加し、また記述量もprintfと比べ格段に増えてしまったため、敬遠するプログラマも多い。

3.2. 文字列ストリーム
-------------

標準ヘッダーのひとつには、文字列を対象としたストリーム操作の機能を提供するクラスistringstream、ostringstreamが定義されている。

3.3. 外部ライブラリ
------------

Boost C++ライブラリにおけるboost::formatは型安全なsprintfであり、出力の際のマニピュレータの記述の煩雑さを軽減する。

4. Javascript
-------------

ストリーム APIを持つ。

5. Node.js
----------

ストリームを扱うモジュールを持つ。

6. Java
-------

ストリーム API　を持つ。

7. .NET Framework
-----------------

.NET Frameworkでも入出力をストリームに一般化しており、ここでは低水準なRead, Writeメソッドのみサポートしている。以下に代表的なクラスを挙げる。

System.IO.Stream一般的なストリームのインターフェイスを提供する抽象クラス。System.IO.FileStreamファイル入出力機能を提供するストリーム。System.IO.MemoryStreamメモリバッファの入出力機能を提供するストリーム。System.Net.Sockets.NetworkStreamネットワークの入出力機能を提供するストリーム。上記のストリームを読み書きするためのクラスが別途存在し、通常はこれらを用いて入出力処理を行なう。

System.IO.BinaryWriter / System.IO.BinaryReaderストリームに対してバイナリ形式での読み書き機能を提供するクラス。System.IO.TextWriter / System.IO.TextReaderストリームに対してテキスト形式での読み書き機能を提供するクラス。System.IO.BufferedStream既存のストリームにバッファリング機能を追加するためのストリーム。

8. 関数型言語
--------

関数型言語においては専ら、無限の大きさの再帰的なデータ構造を指す。遅延評価を用いて実装されるため「遅延ストリーム」とも呼ばれる。無限のデータを扱うには、そのうちの一部を切り出したりする関数も必要なほか、ストリームを成すデータも再帰関数によって生成されるため、オブジェクト指向の関数型言語では、これらをまとめてストリームクラスとして実装することもある。関数が出力するストリーム自体は連結リストとして実装されることが多い。

遅延評価は必要なときに必要なだけ関数を評価し、不要になったら関数の評価を正常に中断することができる。このことは再帰呼び出しを中断する条件の判定を一切行わない再帰関数や再帰的な値の定義を可能にする。たとえば階乗を求める関数は、1から順に再帰的にかけ算してその結果を返すよう定義するだけである。上限などを設ける必要はなく、かわりに「結果をn個求めてリストにする」関数や「結果をx個捨てる」関数などを介して呼び出す。条件判定をしないということは、遅延評価を行わない言語では無限ループに陥いるということである。しかしそのような言語でも無限の要素数を持つデータを扱えるようにするため、ストリームに関してだけ遅延評価を取り入れている。

無限に再帰する関数や値から一部を取り出したりするしくみは巧妙にできている。「結果をn回求める」関数をrepeat、再帰関数をrecとしてその経過を見る。

repeatがrecを1度だけ呼び出す
recは再帰する直前の状態で一旦結果を返す
repeatは結果とともに、recを1回分だけ評価された関数として受け取りリストに連結する
recを呼び出した回数がnでないなら、recをサンクの状態からフォースする
2から4を繰り返す
recの結果を返すサンクは関数を評価した途中経過と見做せる。recを階乗関数factとして3回評価する場合のサンクの状態を示す。 fact関数は fact i = i *  と定義すればよい。

repeat 3 
repeat 3 
repeat 3 
repeat 3 
---> 6カッコの中全体がサンクと言える。また、フォースは「カッコの中と関数をひとつの関数として呼び出すこと」と言える。

遅延評価が標準である言語ではすべての再帰的な定義がストリームのように機能するため、とくにストリームと呼ぶことはない。しかしHaskellにおいては、モナドが導入される以前は、参照透過な入出力を実現するためのデータ構造をストリームと呼んでいた。

9. 脚注
-----

10. 関連項目
--------

標準ストリーム
入出力
ストリーム・プロセッシング
ストリーミング