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

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

ストリーム(stream)とはデータを「流れるもの」として捉え、流れ込んでくるデータを入力、流れ出ていくデータを出力として扱う抽象データ型である。ファイルの入出力を扱うもの、メモリバッファの入出力を扱うもの、ネットワーク通信を扱うものなどさまざまなものがある。特にファイルの入出力には、標準ストリームと呼ばれる特別なストリームが用意されていることもある。

C++[編集]

入出力ストリーム[編集]

<iostream>ヘッダファイルには標準入出力ストリームとしてcoutcincerrclog、およびワイド文字列用のwcoutwcinwcerrwclogが定義されている。

#include <iostream>
int main()
{
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

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

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

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

文字列ストリーム[編集]

<sstream>ヘッダファイルには文字列を対象としたストリーム操作の機能を提供するクラスistringstreamostringstreamが定義されている。

.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
既存のストリームにバッファリング機能を追加するためのストリーム。

関数型言語[編集]

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

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

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

  1. repeatがrecを1度だけ呼び出す
  2. recは再帰する直前の状態で一旦結果を返す
  3. repeatは結果とともに、recを1回分だけ評価された関数(サンク。クロージャの一種)として受け取りリストに連結する
  4. recを呼び出した回数がnでないなら、recをサンクの状態からフォース(強制評価)する
  5. 2から4を繰り返す
  6. recの結果を返す

サンクは関数を評価した途中経過と見做せる。recを階乗関数factとして3回評価する場合のサンクの状態を(概念的にだが)示す。 fact関数は fact i = i * (fact i+1) と定義すればよい。

  1. repeat 3 (fact 1)
  2. repeat 3 (1 * fact 1+1)
  3. repeat 3 (1 * 2 * fact 2+1)
  4. repeat 3 (1 * 2 * 3 * fact 3+1)
  5. ---> 6

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

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

関連項目[編集]