最初に説明するアルゴリズムセットは、主に新規作成されたシーケンスを特定の値で初期化するために使用されます (それ専用というわけではありません)。標準 C++ ライブラリには、いくつかの初期化アルゴリズムが用意されています。初期化アルゴリズムは、コンテナのすべての要素を上書きします。アルゴリズムは、初期化に使用される値のソースによって異なります。fill() アルゴリズムは単一値を繰り返し、copy() アルゴリズムは第 2 のコンテナから値を読み取り、generate() アルゴリズムは新規の値ごとに関数を呼び出します。この節では、これらのアルゴリズムの適用方法の例を挙げて、アルゴリズムの選択方法を提案します。
注 : 以降の節で説明するプログラム例は、alg1.cpp ファイルにあります。 ここに挙げるプログラムの説明では出力文が省略されています。 全ての出力文はプログラムファイルに付属しています。
fill() および fill_n() アルゴリズムは、固定値を使用してシーケンスを初期化または再初期化するために使用されます。宣言は次のとおりです。
void fill (ForwardIterator first, ForwardIterator last, const T&); void fill_n (OutputIterator, Size, const T&);
次のプログラム例は、アルゴリズムのいくつかの使用方法を説明しています。
void fill_example () // fill アルゴリズムの使用方法を説明する // 完全なソースコードについては alg1.cpp を参照 { // 例 1、配列に初期値を入力する char buffer[100], * bufferp = buffer; fill (bufferp, bufferp + 100, '\0'); fill_n (bufferp, 10, 'x'); // 例 2、fill を使用してリストを初期化する list<string> aList(5, "nothing"); fill_n (inserter(aList, aList.begin()), 10, "empty"); // 例 3、fill を使用してリストの値を上書きする fill (aList.begin(), aList.end(), "full"); // 例 4、コレクションの一部を入力する vector<int> iVec(10); generate (iVec.begin(), iVec.end(), iotaGen(1)); vector<int>::iterator & seven = find(iVec.begin(), iVec.end(), 7); fill (iVec.begin(), seven, 0); }
例 1 では、文字値の配列が宣言されます。fill() アルゴリズムが呼び出され、配列内の各位置がヌル文字値で初期化されます。次にアルゴリズム fill_n() の使用により、最初の 10 個の位置が文字 'x' と置き換えられます。fill() アルゴリズムは、引数として開始および終了反復子を必要とするのに対し、fill_n() アルゴリズムは開始反復子とカウントを使用することに注意してください。
例 2 は、挿入反復子 (2.4 節) を適用することにより、fill_n() アルゴリズムを使用して list などの可変長コンテナを初期化する方法を説明しています。この場合、list には最初 5 つの要素が含まれており、そのすべてにテキスト "nothing" が含まれています。fill_n() を呼び出して、文字列 "empty" の 10 のインスタンスを挿入します。結果として得られる list には、15 の要素が含まれます。
例 3 と 4 は、fill() を使用して既存のコンテナの値を変更する方法を説明しています。例 3 では、例 2 で作成された list 内の 15 の各要素が、文字列 "full" で置き換えられます。
例 4 は、list の一部だけを上書きします。アルゴリズム generate() と関数オブジェクト iotaGen (3.3 節 と 13.2.3 節を参照) の使用により、ベクトルが値 1 2 3 ... 10 に初期化されます。 find() アルゴリズム (13.3.1 節を参照) は要素 7 の位置の検索に使用され、反復子内の位置を固有の vector データベース用に保存します。次に、fill() 呼び出しによって、エントリ 7 までの、ただし 7 を含まないすべての値が 0 と置き換えられます。結果として得られるベクトルには 6 つのゼロフィールドがあり、その後に値 7、8、9、10 が続きます。
fill() および fill_n() アルゴリズムは、標準 C++ ライブラリに含まれるすべてのコンテナクラスに使用できますが、挿入反復子は set などの順序付きコンテナに使用する必要があります。
copy() および copy_backward() アルゴリズムは、さまざまな目的に使用することができる多用途の関数であり、標準 C++ ライブラリの中でも最も広く実行されるアルゴリズムです。これらのアルゴリズムの宣言は次のとおりです。
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); BidirectionalIterator copy_backward (BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator result);
copy() 関数によって返される結果は、コピーされるシーケンスの終端のポインタです。ただし、1 つの copy() 演算の結果を後続の copy() の開始反復子として使用して、値の連結を作成することができます。
copy アルゴリズムには次の用途があります。
シーケンス全体を新規シーケンスにコピーして複製する
既存のシーケンスのサブシーケンスを作成する
シーケンスに要素を追加する
シーケンスを入力から、または出力へコピーする
シーケンスをある書式から別の書式へ変換する
次のプログラム例は copy アリゴリズムの使用方法を説明しています。
void copy_example() // copy アルゴリズムの使用方法を説明する // 完全なソースコードについては alg1.cpp を参照 { char * source = "reprise"; char * surpass = "surpass"; char buffer[120], * bufferp = buffer; // 例 1、単純コピー copy (source, source + strlen(source) + 1, bufferp); // 例 2、自己コピー copy (bufferp + 2, bufferp + strlen(buffer) + 1, bufferp); int buflen = strlen(buffer) + 1; copy_backward (bufferp, bufferp + buflen, bufferp + buflen + 3); copy (surpass, surpass + 3, bufferp); // 例 3、出力へコピー copy (bufferp, bufferp + strlen(buffer), ostream_iterator<char,char>(cout)); cout << endl; // 例 4、コピーを使用して型を変換 list<char> char_list; copy (bufferp, bufferp + strlen(buffer), inserter(char_list, char_list.end())); char * big = "big "; copy (big, big + 4, inserter(char_list, char_list.begin())); char buffer2 [120], * buffer2p = buffer2; * copy (char_list.begin(), char_list.end(), buffer2p) = '\0'; cout << buffer2 << endl; }
例 1では、最初の copy() 呼び出しにより、変数 source で示される文字列が単に buffer にコピーされ、その結果、buffer にテキスト "reprise" が含まれます 。コピーの終了位置は終端ヌル文字を越えた位置であり、これによってヌル文字が確実にコピー演算に含まれることに注意してください。
copy() 演算は特に、宛先反復子がソース反復子によって形成される範囲内にある場合に、シーケンスをシーケンスそのものにコピーする自己コピーを使用できるように特に設計されています。これは、例 2 で説明しています。コピーは buffer の位置 2 で開始されて終わりまで展開され、文字を buffer の先頭にコピーします。その結果、buffer に値 "prise" が保存されます。
例 2 の後半は、copy_backward() アルゴリズムの使用方法を説明しています。この関数は copy() アルゴリズムと同じタスクを実行しますが、最初に要素をシーケンスの終わりからシーケンスの先頭に移動します。引数を文字列とみなす場合は、文字は右から左へ移動されます。この場合、buffer に値 "priprise" が割り当てられる結果となります。次に最初の 3 文字が、別の copy() 演算によって値 "sur" に変更され、その結果 buffer に値 "surprise" が保存されます。
注 : copy_backwards アルゴリズムでは、要素そのものではなく、転送順序が逆になることに注意してください。ターゲット内の移動される値の相対位置は、ソース内の位置と同じです。
例 3 は、出力ストリーム (2.3.2 節を参照) への値の移動に使用される copy() を説明しています。この場合のターゲットは、出力ストリーム cout に生成される ostream_iterator です。同じメカニズムを入力値にも使用することができます。たとえば、次の copy() 呼び出しは、入力ストリーム内のすべての語を list にコピーする単純なメカニズムです。
list<string> words; istream_iterator<string, char> in_stream(cin), eof; copy(in_stream, eof, inserter(words, words.begin()));
この手法は、8.3 節で説明したスペルチェックプログラムに使用されています。
コピーは、ストリームの型を別の型に変換する目的にも使用されます。たとえば、例 4 のプログラム例の呼び出しでは、buffer 保存された文字が 1 つずつ文字 list にコピーされます。inserter() の呼び出しによって、挿入反復子が生成され、list への値の挿入に使用されます。最初の copy() 呼び出しで、例 2 で作成された文字列 surprise が list にコピーされます。第 2 の copy() 呼び出しで文字列 "big" から値が list の先頭に挿入され、その結果、list に文字 big surprise が含まれます。最後の copy() 呼び出しは、文字を list から文字バッファにコピーして戻す、逆方向のプロセスを説明しています。
ジェネレータは、連続呼び出しの一連の値を返す関数で、最も一般的なものとして、乱数発生関数があります。ジェネレータは、シーケンスの初期化など、さまざまな用途のために作成することができます。
fill() や fill_n() と同様に、アルゴリズム generate() および generate_n() を使用して、シーケンスを初期化または再初期化することができます。ただし、これらのアルゴリズムは、固定引数の代わりにジェネレータから値を取り出します。これらのアルゴリズムの宣言は次のとおりです。
void generate (ForwardIterator, ForwardIterator, Generator); void generate_n (OutputIterator, Size, Generator);
以下のプログラム例は、シーケンスの初期化のために generate アルゴリズムを使う方法を説明しています。
string generateLabel () { // 書式 L_ddd の一意のラベル文字列を生成する // 完全ソースコードについては alg1.cpp を参照 static int lastLabel = 0; char labelBuffer[80]; ostrstream ost(labelBuffer, 80); ost << "L_" << lastLabel++ << '\0'; return string(labelBuffer); } void generate_example () // generate および generate_n アルゴリズムの使用方法を説明する { // 例 1、ラベル値のリストを生成する list<string> labelList; generate_n (inserter(labelList, labelList.begin()), 4, generateLabel); // 例 2、算術プロセスを生成する vector<int> iVec(10); generate (iVec.begin(), iVec.end(), iotaGen(2)); generate_n (iVec.begin(), 5, iotaGen(7)); }
ジェネレータを、以前の履歴に関する情報を静的な変数に記憶する、単純な関数として作成することができます。その例は、関数 generateLabel() を説明する最初のプログラム例に示してあります。この関数は、コンパイラが必要とするような一意の文字列ラベルのシーケンスを作成します。関数 generateLabel() を呼び出すたびに、それぞれ一意の数値を持つ、書式 L_ddd の新規文字列が生成されます。変数 lastLabel は static と宣言されているため、その値は呼び出しから次の呼び出しへ記憶されます。プログラム例の最初の例は、この関数を generate_n() アルゴリズムと組み合わせて、4 つのラベル値のリストを初期化する方法を説明しています。
第 3 章で説明したとおり、関数は関数呼び出し演算子に応答する任意のオブジェクトです。この定義を使用して、クラスを関数として容易に作成することができます。その一例がクラス iotaGen です (3.3 節を参照)。iotaGen 関数オブジェクトは、整数算術シーケンスのジェネレータを作成します。2 つめのプログラム例では、このシーケンスを使用して、vector が 2 〜 11 の整数値で初期化されます。次に、generate_n() 呼び出しが使用され、ベクトルの最初の 5 つの位置が値 7 〜 11 で上書きされます。その結果、vector は 7 8 9 10 11 7 8 9 10 11 となります。
テンプレート関数 swap() を使用して、同じ型の 2 つのオブジェクトの値を交換することができます。これは次のように定義されます。
template <class T> void swap (T& a, T& b) { T temp(a); a = b; b = temp; }
この関数は、iter_swap() という関数内の反復子に一般化されます。アルゴリズム swap_ranges() は、これをシーケンス全体に拡張します。最初のシーケンスに示される値は、第 2 の並列シーケンスで表示される値と交換されます。swap_ranges() アルゴリズムの記述は次のとおりです。
ForwardIterator swap_ranges (ForwardIterator first, ForwardIterator last, ForwardIterator first2);
第 2 の範囲は、開始反復子のみで定義されています。第 2 の範囲は最初の範囲と同数かそれ以上の要素を含むことが想定されていますが、確認されていません。
注 : アルゴリズムには、2 つの並列シーケンスで処理するものがあります。一般に第 2 のシーケンスは、開始反復子と終了反復子の対ではなく、開始反復子のみで識別されます。第 2 のシーケンスは、最初のシーケンスと同等かそれ以上の規模であることが前提になっていますが、確認されていません。この条件が満たされない場合は、エラーとなります。
プログラム例では、swap() と iter_swap() は単独で、また組み合わせて使用されています。
void swap_example () // アルゴリズム swap_ranges の使用方法を説明する // 完全ソースコードについては alg1.cpp を参照 { // まず 2 つの並列シーケンスを作成する int data[] = {12, 27, 14, 64}, *datap = data; vector<int> aVec(4); generate(aVec.begin(), aVec.end(), iotaGen(1)); // swap と iter_swap を説明する swap(data[0], data[2]); vector<int>::iterator last = aVec.end(); last--; iter_swap(aVec.begin(), last); // シーケンス全体を交換する swap_ranges (aVec.begin(), aVec.end(), datap); }