User Tools

Site Tools


programming:cpp:container

コンテナクラス

STL のコンテナクラスを使いこなせれば処理が楽になることは往々にしてある.
時間があれば適切なコンテナクラスを使うことに挑戦してみるのがよい

以下は自分が使ったもの

vector

C++ の reference はここにある.
要素の順序を保存するクラスであり,要素数が自動的に拡張される一次元配列のイメージ.
添字によるランダムアクセスが高速で行なえ,配列の最後尾に要素を追加していくイメージで使用することが多い.
末尾以外への挿入がメインの操作であったり,シーケンシャルアクセスの速さが必要な場合は list や deque を使うらしい.

for loop の回し方を以下に示しておく.

#include <iostream>
#include <vector>
 
int main(){
 
  std::vector<int> v = {1, 3, 4};
 
  for ( auto iv : v ) {
    std::cout << iv << std::endl;
  }
  // 1
  // 3
  // 4
  v.push_back(5);
 
  for ( auto itr = v.begin(); itr != v.end(); itr++ ) {
    std::cout << *itr << std::endl;
  }
  // 1
  // 3
  // 4
  // 5
 
  v.at(0) = 0;
 
  for ( int i = 0; i < v.size(); i++ ) {
    std::cout << v.at(i) << std::endl;
  }
  // 0
  // 3
  // 4
  // 5
 
  std::cout << v.front << ", " << v.back << std::endl;
  // 0, 5
 
}

その他resize, empty, reserve, clear, shrink_to_fit などの使い方を調べてみるとよい.

array

C++ の reference はここにある.
要素の順序を保存するクラスであり,vector と違って要素数が決まっている.
個人的には vector を使っていればよいと思うので,あまり使っていない.
ROOT では array はバグの温床なので使わないほうがよい.

set

C++ の reference はここにある.
数学における集合に相当し,ユニークな要素を収納するクラス.
ある要素があるかないか」という特徴から探索をすることに特化しており,次の map において要素自身がキーとなっていることに相当する.
「コンテナの何番目」という特徴から探すのであれば vector や array がよい.
要素はユニークでなければならず,複数の要素を許すには multiset を用いる.

たとえば std::set<std::pair<B2EmulsionSummary*, B2EmulsionSummary* > > linklets としてあるグループに含まれる linklet の set を使った.
linklet は重複が考えられるが,重複する linklet を考える意味はなく,また順番よりもある linklet の有無が気になることがあるため. (もちろんアルゴリズムによる)
なお,こんな長い型は本来は適当に以下のように typedef するべきである.

typedef std::set<std::pair<B2EmulsionSummary*, B2EmulsionSummary* > > B2PairSet;

set は二分木で探索する際に要素の順序を用いるため,少なくとも不等号が必要. (default は less<Key>)
自作クラスについてはoperator< を定義する必要がある.
operator のオーバーロードの仕方はここを参照.

#include <iostream>
#include <set>
 
int main (int argc, char *argv[]) {
 
  std::set<int> test_set;
 
  // 要素の追加
  test_set.insert(1);
  test_set.insert(2);
  // 同じ要素は追加されない
  test_set.insert(2);
  // insert の戻り値は std::pair<iterator, bool> になっている.
  // first は追加された場合はその iterator を,そうでない場合はすでにその要素が存在する iterator を指す
  // second は追加された場合は true を,そうでない場合は false を出力する
 
  // 順番にソートされている
  // ただし順番にしたいだけなら vector を sort したほうがよい
  for ( auto itr = test_set.begin(); itr != test_set.end(); itr++ ) {
    std::cout << *itr << std::endl;
  }
  // 1
  // 2
 
  // 値が set に存在するかを確認
  if ( test_set.find(1) != test_set.end() )
    std::cout << "Found" << std::endl;
  else
    std::cout << "Not found" << std::endl;
  // Found
 
  std::exit(0);
 
}

map

C++ の reference はここにある.
ユニークな要素をキーと関連付けて収納するクラス.
要素に対して「あるキーを持っている」という特徴から探索をすることに特化したコンテナクラス.
「コンテナの何番目」という特徴から探すのであれば vector や array がよい.
キーと要素は 1:1 対応でなければならず,あるキーに対して複数の要素を許すには multimap を用いる.

たとえば std::map<int, B2TrackSummary* > track_id_map として track id と B2 class の map を使った.
これは track->GetTrackId() とは別の id が emulsion 側とのインターフェースのために振られているためで,解析のフローを改良するべきだという意見は大いにある.

map は二分木で探索する際にキーの順序を用いるため,少なくとも不等号が必要. (default は less<Key>)
自作クラスについてはoperator< を定義する必要がある.
operator のオーバーロードの仕方はここを参照.

#include <iostream>
#include <map>
 
int main (int argc, char *argv[]) {
 
  std::map<int, std::string> month_map;
 
  // 要素の追加は make_pair を用いて行う
  month_map.insert(std::make_pair(1, "January"));
  month_map.insert(std::make_pair(2, "February"));
  month_map.insert(std::make_pair(3, "March"));
  // 同じキーに対応する要素は追加されない
  month_map.insert(std::make_pair(3, "San-gatsu"));
 
  // キーの順番にソートされている
  for ( auto itr = month_map.begin(); itr != month_map.end(); itr++ ) {
    std::cout << (*itr).first << "月 : " << (*itr).second << std::endl;
  }
  // 1月 : January
  // 2月 : February
  // 3月 : March
 
  // キーの値で月を探索
  std::string second_month = month_map.at(2);
  std::cout << second_month << std::endl;
  // February
 
  std::exit(0);
 
}

multimap

C++ の reference はここにある.
map において一つのキーに対して複数の要素を許すようにしたもの.

#include <iostream>
#include <map>
 
int main (int argv, char *argv[]) {
 
  std::multimap<int, std::string> month_map;
 
  // 要素の追加は make_pair を用いて行う
  month_map.insert(std::make_pair(1, "January"));
  month_map.insert(std::make_pair(2, "February"));
  month_map.insert(std::make_pair(3, "March"));
  // 同じキーに対応する要素は追加される
  month_map.insert(std::make_pair(3, "San-gatsu"));
 
  // あるキーに対応する値をすべて探索
  auto range = month_map.equal_range(3);
  // equal_range の戻り値はキーが対応する最初と最後+1のイテレータの pair
  // std::pair<iterator, iterator> である
  for ( auto itr = range.first; itr != range.second; itr++ ) {
    std::cout << (*itr).second << std::endl;
  }
  // March
  // San-gatsu
 
  std::exit(0);
 
}

unordered_multimap

一般的には hash multimap と呼ばれる.
キーの順番に従って並べられるわけではなく,キーから要素を取り出すことに特化したコンテナクラスとなっている(らしい). std にも導入されているが,自分は boost のものを使用した.

unordered なコンテナを使う際には等号とハッシュ関数が必要.
自作クラスについてはこれらを定義する必要がある.
それぞれの関数のオーバーロードの仕方はここを参照.

#include <iostream>
#include <vector>
#include <utility>
 
#include <boost/unordered_map.hpp>
 
int main (int argc, char *argv[]) {
 
  // linklet の実装.
  // ただし実は MC の解析であれば unordered である必要はなかった.
  boost::unordered_multimap<int, int > linklet;
 
  std::vector<std::pair<int, int > > linklets;
  for ( auto ilinklet : linklets ) {
    if ( ilinklet.first > ilinklet.second )
      std::swap(ilinklet.first, ilinklet.second);
    linklet.insert(std::make_pair(linilet.first, linklet.second));
  }
  // これで上流の basetrack を基準につながる下流の basetrack を高速で探索することが可能となった.
  // ただし,実際には int ではなく,plate などで大小関係が決まった構造体を詰めている
 
  // ある basetrack からつながるすべての basetrack を列挙する.
  int target = 100;
  if ( linklet.find(target) != linklet.end() ) {
    auto range = linklet.equal_range(target);
    for ( auto itr = range.first; itr != range.second; itr++ ) {
      std::cout << (*itr).second << std::endl;
    }
  }
 
  std::exit(0);
 
}
programming/cpp/container.txt · Last modified: 2022/05/06 17:25 by odagawa