工事中...。
○ stack
○ queue
○ priority_queue
○ bitset
○ string
△ valarray
× complex
× rope
ヒューレットパッカード製のSTL(VC6付属品)を利用する場合は、
文字列クラス同士の比較命令でハングする恐れがあるので、
問題が多発する場合は、
char_traits<> の compare メソッドを
memcmp から strncmp に書きかえたものを継承すると良いです。
人が創ったものなので、多少のバグは多かれ少なかれ存在します。
// stack : #include <stack> namespace std { template<class T,class Container = deque<T> >class stack ; } |
デフォルトテンプレートで指定されるコンテナは、deque であるが、これは、dequeがvectorと違い、メモリ管理が自動で行われる点を考慮されたものと考えられる。 敢えてメモリを解放しないのであれば、
stack<int,vector<int> > int_vstack などとしてカプセルコンテナを vector
に指定することも出来る。
スタックでコンテナへのアクセスが提供されるメソッドは、以下の三種類が主に挙げられる。
push() - 要素を先頭へ積む
top() - 先頭の要素を返す
pop() - 先頭から要素を一つ外す
namespace std { template<class T,class Container = deque<T> >class stack {public:typedef typename Container::value_type value_type ;typedef typename Container::size_type size_type ;typedef Container container_type ;protected:Container c ; // カプセルコンテナpublic:expclict stack(const Container & = Container()) ;bool empty() const { return c.empty() ; }size_t size() const { return c.size() ; }void push(const value_type& x) { c.push_back(x); }void pop() { c.pop_back(); }value_type top() { return c.back(); }const value_type& top() const { return c.back(); }template<class T, class Container>bool operator== (const stack<T,Container>&,const stack<T,Container>&);template<class T, class Container>bool operator< (const stack<T,Container>&,const stack<T,Container>&);// operator > <= >= != 省略}; } |
┘
キューは、シーケンスコンテナのメソッドを順番待ちに特化させるために別に生成されたカプセル化インラインテンプレートクラスである。 ここで言う順番とは、レストランの行列待ちと同じで店員が客を扱うように、要素を循環させる事を意味している。 push()で新たに並んだ要素は、先客の要素がpop()して出て行かない限り、その要素は顔を現さないということである。
// queue : #include <queue>namespace std {template<class T,class Container = deque<T> >class queue ;}デフォルトテンプレートで指定されるコンテナは、deque であるが、これは、dequeが順番待ちに一番適したコンテナであることが考慮されたものと考えられる。 例えば、listでそれを敢えて再現したいのであれば、 queue<int,list<int> > int_lqueue などとしてカプセルコンテナをlistに指定することも出来る。
キューでコンテナへのアクセスが提供されるメソッドは、以下の四種類が主に挙げられる。スタックとの違いは、push()で追加されていった要素を逆順ではなく順番どおりに取り出してゆく所にある。
push() - 要素を並びの最後に追加するfront() - 先頭の要素を返すback() - 最後の要素を返すpop() - 先頭から要素を一つ外す
queue についても、構造を明記した方が分かり易いだろう。 front/back/pop 以外は、stack と同じような構造をしている。
namespace std {template<class T,class Container = deque<T> >class queue {public:typedef typename Container::value_type value_type ;typedef typename Container::size_type size_type ;typedef Container container_type ;protected:Container c ; // カプセルコンテナpublic:expclict queue(const Container & = Container()) ;bool empty() const { return c.empty() ; }size_t size() const { return c.size() ; }void push(const value_type& x) { c.push_back(x); }void pop() { c.pop_front(); }value_type front() { return c.front(); }const value_type& front() const { return c.front(); }value_type back() { return c.back(); }const value_type& back() const { return c.back(); }template<class T, class Container>bool operator== (const queue<T,Container>&,const queue<T,Container>&);template<class T, class Container>bool operator< (const queue<T,Container>&,const queue<T,Container>&);// operator > <= >= != 省略};}
┘
優先度付きキューは、シーケンスコンテナのメソッドを順番待ちに特化させ、かつ優先順に内部にヒープを保持したカプセル化インラインテンプレートクラスである。 内部のコンテナ要素は常にヒープソートで優先順が保たれている。
// priority_queue : #include <queue>namespace std {template< class T,class Container = vector<T>,class Compare = less<typename Container::value_type> >class priority_queue ;}デフォルトテンプレートで指定されるコンテナは、vector であるが、これにはあまり深い意味がない。 dequeでそれを敢えて再現したいのであれば、 priority_queue<int,deque<int>,greater<int> > int_dgpqueue などとしてカプセルコンテナをdequeに指定することも出来る。
優先度付きキューでコンテナへのアクセスが提供されるメソッドは、以下の3種類が主に挙げられる。push()で追加されていった要素は、常に優先度付きでソートされる。
push() - 要素を並びに追加してヒープソートするtop() - 優先度最大の要素を返すpop() - 優先度最大の要素を一つ外す
priority_queue についても、stack/queue と同じような構造をしている。 初期化と要素が挿入される部分で make_heap/push_heap/pop_heap を利用してヒープソートされる辺りが違う。
namespace std {template<class T,class Container = vector<T>,class Compare = less<typename Container::value_type> >class priority_queue {public:typedef typename Container::value_type value_type ;typedef typename Container::size_type size_type ;typedef Container container_type ;protected:Compare comp ; // 比較準拠オブジェクトContainer c ; // カプセルコンテナpublic:expclict priority_queue(const Compare & cmp = Compare(),const Container & cont = Container() ): comp(cmp), c(cont) {make_heap(c.begin(),c.end(),c.end(),comp) ;}template<class InputIterator>expclict priority_queue(InputIterator first, InputIterator last,const Compare & cmp = Compare(),const Container & cont = Container() ): comp(cmp), c(cont) {c.insert(c.end(),first,last) ;make_heap(c.begin(),c.end(),c.end(),comp) ;}bool empty() const { return c.empty() ; }size_t size() const { return c.size() ; }void push(const value_type& x) {c.push_back(x);push_heap(c,c.begin(),c.end(),comp) ;}void pop() {pop_heap(c.c.begin(),c.end(),comp) ;c.pop_back();}value_type top() { return c.front(); }};}
┘
ビット(0と1)の配列と演算に特化したコンテナクラス。
// bitset : #include <bitset>namespace std {template< size_t Bits >class bitset ;}<例1> enum フラグとして利用
enum TMyColor { mcRed,mcGreen,mcBlue,mcMax } ;// enum 値に相互に対応したbitsetの生成bitset<mcMax> ColorBits ;// 赤と青のビットを立てるColorBits.set(mcRed) ;ColorBits.set(mcBlue) ;// ColorBits のビットが立っているかどうかif(ColorBits.any()) {//すべてのフラグを調べるfor(int c= 0 ; c < mcMax ; c++ ) {//ビットが立っているかどうかif(ColorBits[(TMyColor)c]) {・・・}}}<例2> 2進表記文字列としての bitset
//2進数による出力 cout << "267 as binary short: " << bitset<numeric_limits<unsigned short>::digits>(267) << endl; cout << "267 as binary long: " << bitset<numeric_limits<unsigned long>::digits>(267) << endl; cout << "10,000,000 with 24 bits: " << bitset<24>(1e7) << endl; //2進表現を整数に変換 cout << "\"1000101011\" as number: " << bitset<100>(string("1000101011")).to_ulong() << endl;
出力結果 267 as binary short: 0000000100001011267 as binary long: 00000000000000000000000100001011"1000101011" as number: 555● bitset コンストラクタ/デストラクタ
bitset<bits> cすべてのビットを0で初期化したbitsetを生成する。 bitset<bits> c(unsigned long value)下位ビットをvalueで満たしたbitsetを生成する。 valueでは足りない上位ビットは0で初期化される。 bitset<bits> c(const string& str) //explictbitset を生成し、strという2進表記文字列を解析して初期化する。 ビットに足りない上位ビットは0で埋め尽くされる。 bitset<bits> c(const string& str,string::size_type str_idx)bitset を生成し、strという2進表記文字列を解析して初期化する。 その際、2進の解析は、str_idxで指定された文字列インデックスから最後まで行われる。 ビットに足りない上位ビットは0で埋め尽くされる。 bitset<bits> c(const string& str,string::size_type str_idx,string::size_type str_num)bitset を生成し、strという2進表記文字列を解析して初期化する。 その際、2進の解析は、str_idxで指定された文字列インデックスからstr_num個の文字数を有効数として行われる。 ビットに足りない上位ビットは0で埋め尽くされる。 c.~bitset<bits>()bitsetを破棄してメモリを解放する。 ● bitset 変更を行わない演算(const)
● bitset 変更を行う演算(constではない)
● 変更を加えたbitsetの複製(const)
● bitsetの型変換
┘
●basic_string<>
STL文字列コンテナの本体は、basic_string<>を基底としている。
// basic_string<> : #include <string>namespace std {template< class charT,class traits = char_traits<charT>,class Allocator = allocator<charT> >class basic_string ;}charT は、文字列の文字の型を表す。 traits は、文字の演算や比較、コピーの仕方を記述した特性クラスを意味する。
●string/wstring
stringヘッダでは、一般的な文字列クラスを表す型情報を以下のように string/wstring として事前定義している。
// string/wstring : #include <string>namespace std {typedef basic_string<char> string ;typedef basic_string<wchar_t> wstring ;}char型/ワイドchar型の基本的な2種類の文字列クラスを表している。
┘
string s空文字列の s を生成する。 string s(str)str 文字列を複製した s を生成する。 string s(str, stridx)str 文字列のインデックス stridx から始まる文字列の最後までを複製した s を生成する。 string s(str, stridx, strlen)str 文字列のインデックス stridx から始まる文字列の文字数 strlen 分を複製した s を生成する。 string s(cstr)Cの文字列(NULLで終わるchar*)の複製した s を生成する。 string s(cstr, strlen)Cの文字列配列(char*)の strlen 文字数分複製した s を生成する。 string s(beg, end)[beg,end) の範囲の文字で初期化した s を生成する。 s.~string()全ての文字を破棄し、メモリを解放する。
┘
文字列クラスの内容をC言語に準拠した文字列へ還元する方法は、下記の3つの方法が存在する。<例>
- data() メソッドを利用する。 このメソッドは、文字列をCの配列として返す。 注意しなければならないのは、文字の終わりにヌルターミネート '\0' を付加しない点。
- c_str() メソッドを利用する。 このメソッドは、文字列をCの文字列へと復元する。 文字列の終わりには、ヌルターミネート '\0' が付加される。
- copy() メソッドを利用する。 このメソッドは、 C配列へ文字列を複写する。 ただし、 '\0' は付加されないので注意。
int number = atoi(s.c_str()) ; // 文字列を整数に変換fwrite(s.data(),sizeof(char),s.length(),fp) ; // ストリームへ出力s.copy(buffer,100) ; // buffer配列へ100文字出力s.copy(buffer,80,20) ; // buffer配列へ21文字目から80文字出力
┘
文字列型のサイズについては、次の3種類が存在する。reserve() メソッドについては、次の2種類が存在する。
- size() と length() については、両方とも現在格納されている文字数を返す。 2つの関数の戻り値は等価であるのでどちらを利用しても構わない。 また、文字数が存在するかどうかを確かめるには、empty() メソッドを利用すれば良いしこの方がある程度処理速度が速い。
- max_size() 一つの文字列空間に対して最大の格納できる領域の大きさを表した数値を返す。 通常はインデックス表現が出来る最大の整数要素(INT_MAX等)だが、環境によっては、max_size() を利用し、割り当てられる領域に制限を加えているものも中にはある。
- capacity() 内部割り当ての再割り当てを行わずに文字が格納できる最大数を返す。 reserve() メソッドを利用するとこのサイズに事前的な割り当てサイズを指定できる。
s.reserve(800) ; // 800文字分予約する。s.reserve() ; // 現在の文字数に縮小して再割り当てを行う。
┘
データ要素へのアクセスについては、イテレータでアクセスする他に配列へのアクセスがサポートされる。
- s[index] アクセスが許される範囲は、0〜length() まで。 const string 型は、 0〜length()-1 となっている。 const でない string については、length() 位置はヌルターミネート '\0' を返す。 文字の範囲を超した範囲の index については、例外が送出されないため、未定義のふるまいとなる。 アンバサンド(&)演算子を利用すると、その文字のポインタの位置を取得できるが、データの再割り当てが行われると無効になる( e.g. &s[index] )。
- s.at(index) アクセスが許される範囲は、0〜length()-1 まで。 その範囲を超した場合は、out_of_range 例外を発生させる。
┘
文字列を比較するには、次の2通りの方法がある。 どちらも辞書順序優先の比較が行われる。
- s1 op s2 比較演算子でコンペアする。 opには、< > <= >= == != が指定できる。 s1 、 s2 どちらか一方が char* 型でも構わない。
- s1.compare(s2) 辞書順序優先での比較結果をそのまま返す。 要するに、 strcmp(s1.c_str(),s2.c_str()) と同じ結果を返す。 compare のフォーマットには他に、 s1.compare(s1_idx, s1_len, s2) / s1.compare(s1_idx, s1_len, s2, s2_idx, s2_len) のように、インデックスと文字数を指定して比較するメソッドを用意している。 s2側は、char* 型でも構わない。
┘
●代入
文字列を変更して新しい値を代入するには、 = 演算子か、assign() メソッドを利用する。= 演算子の場合、s2は、文字列クラス以外に、 char * 型、 char 型でも構わない。s1 = s2 ;一番目の方法は、s2 の内容を s1 にそのまま代入する。 二番目の方法は、s2 の指定したインデックスから指定文字数分を代入する。 三番目の方法は、 cという文字の複製をnum個作成した文字列を代入する。 四番目の方法は、[beg,end)間の文字を代入する。 s2は、char*型でも構わない。s1.assign(s2) ;s1.assign(s2,s2_idx,s2_len) ;s1.assign(num,c);s.assign(beg,end);
●値の交換
値の内容を交換するには、swap() メソッドを利用する。以前説明した通り、swap() はメモリの参照等の交換だけで済むので非常に高速であることを保証している。s1.swap(s2) ;
●文字列のクリア
文字の消去については、3通りの方法が存在する。 処理内容についてどれも同じである。s = "" ;s.clear() ;s.erase();●文字列の挿入と削除
『追加』については、+=演算子、append()、push_back() メソッドを利用する。『挿入』については、insert() メソッドを利用する。s1 += s2 ... ; // s2 は、"AAA" , 'A' でも構わない。s1.append(s2) ; // s2 は、"AAA" でも構わない。s1.append(s2,s2_idx,s2_len) ;s.push_back(c); // 'A'『削除』については、erase() メソッドを利用する。s1.insert(s1_idx,s2) ; // s2 は、"AAA" でも構わない。s1.insert(s1_idx,s2,s2_idx,s2_len);s.insert(pos,c) ; // pos 反復子の位置に c を挿入。s.insert(idx,num,c) ; // c文字 の複製 n 個 idx 位置へ挿入。s.insert(pos,num,c) ;上記の2種類は、idx と pos が NULL の時にコンフリクトを起こすので、インデックス値0の場合は、敢えて型を明記しなければならない。s.insert((std::string::size_type)0,num,c) ;s.insert(pos,beg,end) ; // 反復子を利用した挿入。『置換』については、replace() メソッドを利用する。s.erase(idx) ; // idx の位置の文字を抹消する。s.erase(idx,num) ; // idx から始まる文字を num 文字抹消する。s.erase(beg,end) ; // 反復子を利用した削除。s1.replace(s1_idx,s1_repnum,s2) ; // s2 は、"AAA" でも構わない。上記は、s1_idx 位置から始まる s1_repnum 個分の部分文字列を s2 で置き換える。
s1.replace(beg,end,s2 または、c) ; // 反復子を利用した置換。s1.replace(beg,end,s2,num) ;s1.replace(beg,end,newBeg,newEnd) ;
┘
部分文字列の『抽出』については、substr() を利用する。文字列の『結合』については、+演算子を利用する。s.substr() ; // 文字列全体の複製s.substr(idx) ; // idx 位置から残り最後まで。s.substr(idx,num) ; // idx 位置から num 文字分。s = s1 + s2 + s3 + ....
┘
ストリームに文字列を出力する場合は、 << , >> 演算子を利用する。また、ストリームから1行を読み込むには、getline() を利用する。std::cin >> s ;std::cout << s ;改行以外文字をトークンセパレータとして利用したい場合は、上記 c のように指定すると働く。while(getline(std::cin,s)) ... ;while(getline(std::cin,s,c)) ... ; // c は、'A'のような文字型。
┘
文字列を検索するメソッドは、多数存在するが、引数等は全く同じもので名前で意味が異なってくる。
( ) に指定される引数は、次の通り:
1番目の引数には、探す文字列を指定する。どのメソッドも見つからなければ、string::npos を返す。
2番目の引数には、探索開始のインデックスを指定する。(オプション)
3番目の引数には、探索開始のインデックスからの探索有効文字数を指定する。(オプション)
┘
文字列も他のコンテナ同様に反復子を取得し利用できる。
s.begin()最初の文字のランダムアクセス反復子を返す。 s.end()最後の文字の次の位置を示すランダムアクセス反復子を返す。 s.rbegin()逆の反復処理を行う際の、最初の文字を示す逆反復子を返す(つまり一番最後の文字の位置)。 s.rend()逆の反復処理を行う際の、最後の文字の次の位置を示す逆反復子を返す(つまり一番最初の文字よりも一つ前の位置)。
┘
文字列テンプレートの2番目のテンプレート引数 char_trais<> を書き換えると、比較や検索/コピーの仕方に対応できる。
以下は、デフォルトのcharの特性テンプレート。
// char_traits<> : #include <string>namespace std {template<charT> struct char_traits<char> {・・・};template<> struct char_trais<char> { //型定義 typedef char char_type ; //文字の型 typedef int int_type ; //EOF表記の型 typedef streampos pos_type ; //ストリーム位置の型 typedef streamoff off_type ; //ストリーム位置間の型 typedef mbstate_t state_type ; //マルチバイトストリームの状況型 //関数 static void assign(char& c1, const char& c2) { // c2 の文字を c1 に代入する c1 = c2 ; } static bool eq(const char& c1, const char& c2) { // c1 の文字と c2 が等しいかどうか return c1 == c2 ; } static bool lt(const char& c1, const char& c2) [ // c1 の文字が c2 より小さいかどうか return c1 < c2 ; } static size_t length(const char* s) { // s 文字列の長さを返す return strlen(s) ; } static char* compare(const char* s1, const char* s2, size_t n) { // s1 文字列と s2 文字列を n 文字比較する return memcmp(s1,s2,n) ; } static char* copy(char* s1, const char* s2, size_t n) { // s2 の文字列から s1 へ n 文字複写する return (char*)memcpy(s1,s2,n) ; } static char* move(char* s1, const char* s2, size_t n) { // s2 の文字列から s1 へ n 文字複写する(重複の許可) return (char*)memmove(s1,s2,n) ; } static char* assign(char* s, size_t n, char c) { // s を n 文字分 c 文字で満たす return (char*)memset(s,c,n); } static const char* find(const char* s, size_t n, const char& c) { // s 文字列から c を探してそのポインタを返す return (const char*)memchr(s,c,n); } static int eof() { // ストリームの終端(EOF)を返す return EOF ; } static int to_int_type(const char& c) { // c を int(ストリーム)表記として正しく返す return (int)(unsigned char)c ; } static char to_char_type(const int& i) { // i を char 表記として正しく返す return (char) i ; } static int not_eof(const int& i) { // EOF である場合は、EOF以外を、それ以外はそのまま返す reutrn i!=EOF ? i: !EOF ; } static bool eq_int_type(const int& i1, const int& i2) { // ストリーム文字の等価比較 reutrn i1 == i2 ; } };}
これを継承して作成する簡単な例として、下記のように大/小文字を区別しない文字列特性を自作することも出来る。
//文字列の大/小文字を区別しない文字列特性クラスstruct ignorecase_traits : public std::char_traits<char> { // c1 の文字と c2 が等しいかどうか static bool eq(const char& c1, const char& c2) { return std::toupper(c1)==std::toupper(c2); } // c1 の文字が c2 より小さいかどうか static bool lt(const char& c1, const char& c2) { return std::toupper(c1)<std::toupper(c2); } // s1 文字列と s2 文字列を n 文字比較する static int compare(const char* s1, const char* s2, std::size_t n) { for (std::size_t i=0; i<n; ++i) { if (!eq(s1[i],s2[i])) { return lt(s1[i],s2[i])?-1:1; } } return 0; } // s 文字列からc を探してそのポインタを返す static const char* find(const char* s, std::size_t n, const char& c) { for (std::size_t i=0; i<n; ++i) { if (eq(s[i],c)) { return &(s[i]); } } return 0; } }; // 文字列を比較しない文字列型の定義 typedef std::basic_string<char,ignorecase_traits> icstring; // この型用に用意した出力ストリーム用の << 演算子の定義 std::ostream& operator << (std::ostream& strm, const icstring& s) { // string 型に変換して偽装工作 return strm << std::string(s.data(),s.length()); }
// valarray<> : #include <valarray>namespace std {template<class T> class valarray ; // T型の整数配列class slice ; // 2次元スライスtemplate<class T> class slice_array ;class gslice ; // 多重構造のスライスtemplate<class T> class gslice_array ;template<class T> class mask_array ; // マスクしたvalarraytemplate<class T> class indirect_array ; // 間接参照のvalarray}
- valarray は、主に利用される中核クラス。
- slice, gslice は、valarray を行列等で使用する多次元構造の一部を抽出するために用いる。
- slice_array, gslice_array, indirect_array は、内部で利用される型情報でユーザーインターフェイスとしては利用できない。
┘
valarray<T> va空の valarrray を生成する。 resize(n) で設定すると後で容量が調整できる。 valarray<T> va(num)T型のデフォルトコンストラクタで作成された値を num 個含む valarray を生成する。 valarray<T> va(value,num)T型の value を num 個含む valarray を生成する。 valarray<T> va(array,num)T*型の array ポインタから始まる num 個の配列を複写して valarray を生成する。 valarray<T> va(va2)va2 というもう一つの valarray を複写して初期化した valarray を生成する。 va.~valarray<T>()全ての値を破棄し、メモリを解放する。
┘
valarray は、主に、配列の一括演算を迅速に処理するためのものである。 ここでは、その演算方法について色々と述べていこう。valarray は、ランダムアクセス機能を搭載したコンテナと同様に [ ] 演算子を利用できる。
また、valarray 同士を計算に利用することも出来る。va[0] = 3 * va[1] + va[2] ;上記は、下記と同じ演算結果をもたらす。va1 = 4 * va2 + va3 ;演算に利用される型は、必ず一致していなければならない。va1[0] = 4 * va2[0] + va3[0] ; va1[1] = 4 * va2[1] + va3[1] ; va1[2] = 4 * va2[2] + va3[2] ; ・ ・ ・ va1[n-1] = 4 * va2[n-1] + va3[n-1] ;上記の演算は、比較演算子についても同じことが言える。 通常コンテナでの比較では結果は一つだが、valarray コンテナでは同じ要素分の論理値が格納できる valarray が必要となる。std::valarray<double> va(20) ; va = 4 * va ; // エラー( 4 は、整数であるので型が一致しない。) va = 4.0 * va ; // OK上記もまた、下記と同じ演算結果をもたらす。std::valarray<double> va1(10) ; std::valarray<double> va2(10) ; std::valarray<bool> vaResult(10) ; … vaResult = (va1 == va2) ;すべての値を同じ値にするには、下記のように値を直接代入する。vaResult[0] = ( va1[0] == va2[0] ) ; vaResult[1] = ( va1[1] == va2[1] ) ; vaResult[2] = ( va1[2] == va2[2] ) ; ・ ・ ・ vaResult[9] = ( va1[9] == va2[9] ) ;また、付属メソッドの min(),max(),sum() を利用すると 最小/最大/合計 を求めることが出来る。va = value ;valarray は、この他にも要素全体をシフトして新しい valarray を返すメソッドも用意している。min_value = va.min() ; max_value = va.max() ; total_value = va.sum() ;num が正の場合は、先頭方向へ。負の場合は、末尾方向へシフトする。newva = va.shift(num) ;newva = va.cshift(num) ;
shift では、先頭方向へシフトする場合は、先頭にあった要素は切り捨てられる。逆に、末尾方向へシフトした場合は、デフォルトコンストラクタで生成された要素が先頭へ挿入される。
cshift では、どちらの方向でも要素の削除や追加は行われずに単に要素を循環させる。
┘
valarray については、配列の一括演算を行えるのと同じように超越関数( valarray に対して行われる一括算術関数)をいくつか用意している。上記は、下記と同じ演算結果をもたらす。vaResult = sqrt(va1) + va2 ;デフォルトで用意されている超越関数は、以下の通り...vaResult[0] = sqrt(va1[0]) + va2[0] ; vaResult[1] = sqrt(va1[1]) + va2[1] ; vaResult[2] = sqrt(va1[2]) + va2[2] ; ・ ・ ・ vaResult[n-1] = sqrt(va1[n-1]) + va2[n-1] ;valarray abs (const valarray& va) valarray pow (const valarray& va1, const valarray& va2) valarray pow (const valarray& va, const T& value) valarray exp (const valarray& va) valarray sqrt (const valarray& va) valarray log (const valarray& va) valarray log10 (const valarray& va) valarray sin (const valarray& va) valarray cos (const valarray& va) valarray tan (const valarray& va) valarray sinh (const valarray& va) valarray cosh (const valarray& va) valarray tanh (const valarray& va) valarray asin (const valarray& va) valarray acos (const valarray& va) valarray atan (const valarray& va) valarray atan2 (const valarray& va1, const valarray& va2) valarray atan2 (const valarray& va, const T& value) valarray atan2 (const valarray& value, const valarray &va)┘
表題で説明した通り、設計者が途中で撤収したため、valarray のサブセットは、完全とは言えない部分が存在する。 slice や gslice で生成された slice_array 等同士をそのまま演算しようとするとコンパイルエラーが発生する。 これは、演算子がサポートされていないために起こる。上記のサブセットの演算を正しく動作させるためには、下記のようにvalarrayに戻す静的型キャストをほぼ常時利用しなければならない。va[std::slice(0,2,3)]= va[std::slice(1,2,3)] * va[std::slice(2.2.3)] ; // エラーもっと曖昧なキャスト表現では、va[std::slice(0,2,3)]= static_cast<std::valarray<double> >(va[std::slice(1,2,3)]) *static_cast<std::valarray<double> >(va[std::slice(2,2,3)]) ;となるが、これではあまりにも不憫すぎるので、暗黙の型変換用インラインを一つ用意しておく。va[std::slice(0,2,3)]= std::valarray<double>(va[std::slice(1,2,3)]) *std::valarray<double>(va[std::slice(2,2,3)]) ;これを利用すると、下記のように表記できる。template <class T> inlinestd::valarray<typename T::value_type> VA(const T& valarray_subset){ return std::valarray<typname T::value_type> (valarray_subset); }上記の場合、暗黙な型変換のための複製が生成される結果となるので、パフォーマンスの影響は回避できない。va[std::slice(0,2,3)]= VA(va[std::slice(1,2,3)]) *VA(va[std::slice(1,2,3)]) ;
パフォーマンスの影響を考慮に入れるのであれば、型を明確にすべきところだろう。このように、サブセットを利用する上では、注意が必要である。typedef valarray<double> DBLVA ;va[std::slice(0,2,3)]= DBLVA(va[std::slice(1,2,3)]) *DBLVA(va[std::slice(2,2,3)]) ;
●スライス
スライス(slice)には、下記の3つの意味を持つ引数を渡して利用する。たとえば、slice を利用して 5 という位置から 7 という間隔で跳々に 4 つの要素を持ったインデックス集合は下記のようになる。
- 開始インデックス
- 要素数(サイズ)
- 要素間の距離(ストライド) ※負の値でも構わない。
このインデックス集合を利用してサブセットを得るには、valarray の [ ] 演算子の引数としてこれを渡す。std::slice(5,4,7) // 5,12,19,26 というインデックス集合この変換されたサブセットは、コンスタントでなければ、slice_array というクラス構造体へ変換される。va[std::slice(5,4,7)]
// valarray<>::operator[]の一部 : #include <valarray>namespace std {template <class T>class valarray {public://コンスタントなスライスセットvalarray<T> operator[] ( slice ) const ;// スライスサブセットslice_array<T> operator[] ( slice ) ;} ;}slice_array では、次の演算が定義されている。
逆に言えば、これ以外の演算を行おうとするとコンパイルエラーが発生するので、前書きに書いたような valarray への強制的な型キャストが必要になる。
- すべての要素に、一つの値を代入する。
- 別の valarray ( または、サブセット ) を代入する。
- += や *= などの計算を行う代入演算子類。
上記の演算は、下記と同じ意味になる。va[std::slice(2,3,4)] = value ;足し算を利用したりする場合は、前述で示した通り、valarray へキャストしてやる必要がある。va[2] = value ; va[6] = value ; va[10] = value ;図で表すと、以下のようになる。va[std::slice(0,2,3)]= VA(va[std::slice(1,2,3)]) +VA(va[std::slice(2,2,3)]) ;
va[0] = va[1] + va[2] ; va[3] = va[4] + va[5] ;●多重構造のスライス
スライスは、二次元に対する要素の切り分けを行っていたが、多重構造のスライスは、多次元構造にネストした要素の切り分けを行うことが出来る。 多重構造スライス gslice に受け渡す3つのパラメータは、slice と変わらないが、2番目と3番目の引数が valarray<size_t>型の 配列である点が違う。
- 開始インデックス
- valarray<size_t>型の要素数(サイズ)
- valarray<size_t>型の要素間の距離(ストライド)
// valarray<>::operator[]の一部 : #include <valarray>namespace std {template <class T>class valarray {public://コンスタントな多重構造のスライスセットvalarray<T> operator[] ( const gslice& ) const ;// 多重構造のスライスサブセットgslice_array<T> operator[] ( const gslice& ) ;} ;}例えば、 以下のような状態の値を gslice に渡すとする。
gslice は、以下のようなインデックス情報を返す。
- 開始インデックス = 4
- 要素数 = valarray<size_t> 型であり、 3, 2, 5 の要素が順に入っているとする。
- 要素間の距離 = valarray<size_t> 型であり、 30,10,7 の要素が順に入っているとする。
使い方は、slice の 2番目と3番目のパラメータが違うだけで全く同じである。4 11 18 25 32 14 21 28 35 42 34 41 48 55 62 44 51 58 65 72 64 71 78 85 92 74 81 88 95 102
●マスクを利用したサブセット
サブセットを得る方法として、スライスの他にブール値をマスクとして利用した mask_array というものがある。 これもコンスタントについては、valarray 自体が返却する。 それ以外は mask_array で返却される。
// valarray<>::operator[]の一部 : #include <valarray>namespace std {template <class T>class valarray {public:// コンスタントなマスクセットvalarray<T> operator[] ( const valarray<bool>& ) const ;// 論理値でマスクしたサブセットmask_array<T> operator[] ( const valarray<bool>& ) ;} ;}論理値のサブセットは、結果が真であるもの意外を切り捨てる。 つまり、
は、100よりも小さい要素の集積となる。 valarray の比較演算子については、配列の一括演算でも説明した通り、valarray<bool> を自動的に生成するので上記のように書くとそのような意味を持った論理値をマスクしたサブセットを結果として得ることができる。va[ va < 100 ]
●間接的なサブセット
インデックス構造を指定して間接的にサブセットを得ることもできる。 これもコンスタントについては、valarray 自体が返却する。 それ以外は indirect_array で返却される。
// valarray<>::operator[]の一部 : #include <valarray>namespace std {template <class T>class valarray {public:// コンスタントなインデックスセットvalarray<T> operator[] ( const valarray<size_t>& ) const ;// インデックスで指定したサブセットindirect_array<T> operator[] ( const valarray<size_t>& ) ;} ;}間接的なサブセットとは名前だけで、インデックス構造を指定した要素をそのままサブセットとして返す。 間接が何処にかかっているかというと、要素をインデックスが間接的に示されているところを言っている。 いわゆる、valarray のインデックスによる間接である。
上記のプログラムは、結果として 70, 50, 20, 30 を 画面に出力する。valarray<int> va(10) ; for(int i=0;i<va.size();i++) va[i] = i*10 ; valarray<size_t> idx(4) ; idx[0] = 7 ; idx[1] = 5 ; idx[2] = 2 ; idx[3] = 3 ; for(int i=0;i<idx.size();i++) cout << VA(va[idx])[i] ; cout << endl ;
┘