株式会社エス・スリー・フォー

stx::bag<Key,Compare,Allocator>

‘bag’について

‘bag’とはmultisetとmapの’あいのこ’です。multisetは要素の重複を許します。たとえば、

multiset<int> ints;
for ( int i = 0; i < 1000; ++i )
  ints.insert(1);

このコードでは、intsの中に1が1000個格納されます。

ここに紹介するクラス’bag’は要素とその個数を管理することにより、格納される要素の個数を最小限に抑えるコンテナです。

bagによる金種計算プログラムを書いてみました。

/*
 * bag demonstration : 金種計算
 */

#include <iostream>       // cout, endl
#include <iomanip>        // setw
#include <set>            // set<T>
#include "bag" // bag<T>

using namespace std;
using namespace stx;

int main() {

  set<int> cash;
  cash.insert(10000);  // 1万円札
  cash.insert( 5000);  // 5千円札
//cash.insert( 2000);  // 2千円札
  cash.insert( 1000);  //  千円札
  cash.insert(  500);  // 5百円玉
  cash.insert(  100);  //  百円玉
  cash.insert(   50);  //  50円玉
  cash.insert(   10);  //  10円玉
  cash.insert(    5);  //   5円玉
  cash.insert(    1);  //   1円玉

  bag<int> kind;

  while ( true ) {
    int price;
    cout << "金額を入力してください (0 で終了) :" << flush;
    cin >> price;
    if ( price <= 0 ) break;
    for ( set<int>::reverse_iterator it = cash.rbegin();
          it != cash.rend(); ++it ) {
      int n = price / *it;
      price %= *it;
      kind.insert_n(n, *it);
      if ( n > 0 ) cout << setw(5) << *it << " : "
                        << setw(3) << n << endl;
    }
  }

  cout << "総計..." << endl;
  for ( set<int>::reverse_iterator it = cash.rbegin();
          it != cash.rend(); ++it ) {
    cout << setw(5) << *it << " : "
         << setw(3) << kind.count(*it) << endl;
  }

  return 0;
}

コンストラクタ

explicit bag(const Compare& comp =Compare(), const Allocator& alloc =Allocator());

デフォルトコンストラクタ。

// sample
bag<string> b;
int n = b.size(); // n = 0
template<class InputIterator>
bag(InputIterator first, InputIterator last, const Compare& comp=Compare(), const Allocator& alloc =Allocator());

シーケンス[first,last)からなるbagを作ります。

// sample
string s = "hello";
bag<char> b(s.begin(), s.end()); // b = { e h l l o }
bag(const bag& x);

コピーコンストラクタ。 x の全要素がコピーされます。

// sample
bag<string> b1;
b1.insert_n(2,"apple");
b1.insert("banana"); // b1 = { apple apple banana }
bag<string> b2(b1);// b2 = { apple apple banana }

デストラクタ

~bag();

デストラクタ。 全要素を廃棄します。

コピー

bag& operator=(const bag& x );

全要素を廃棄し、新たに x の全要素がコピーされます。

// sample
bag<string> b1;
bag<string> b2;
b1.insert_n(2,"apple");
b1.insert("banana"); // b1 = { apple apple banana }
b2 = b1;// b2 = { apple apple banana }

イテレータ

iterator begin();
const_iterator begin() const;

最初の要素を指すイテレータを返します。

iterator end();
const_iterator end() const;

最後の要素の直後を指すイテレータを返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert("banana");
bag<string>::iterator i = b.begin();
while ( i != b.end() ) { // output : apple apple banana
  cout << *i << ' ';
  ++i;
}
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

最後の要素の直後を指す反転イテレータを返します。

reverse_iterator rend();
const_reverse_iterator rend() const;

最初の要素を指す反転イテレータを返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert("banana");
bag<string>::reverse_iterator i = b.rbegin();
while ( i != b.rend() ) { // output : banana apple apple
  cout << *i << ' ';
  ++i;
}

empty / size / max_size

bool empty() const;

要素数が 0 なら true を返します。

// sample
bag<string> b;
bool t0 = b.empty(); // t0 = true
b.insert("apple");
bool t1 = b.empty(); // t1 = false
size_type size() const;

要素数を返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert("banana");
int s = b.size(); // s = 3
size_type max_size() const;

格納できる最大要素数を返します。

insert

void insert_n(iterator position, size_type n, const key_type& x);

要素 x を n 個追加します。 position に意味はありません。

// sample
bag<string> b;
b.insert_n(b.begin(), 5, "apple"); // "apple"を5個追加
int s = b.size() // s = 5
void insert(iterator position, const key_type& x);

要素 x を 1 個追加します。 position に意味はありません。

// sample
bag<string> b;
b.insert(b.begin(), "apple"); // "apple"を1個追加
int s = b.size() // s = 1
void insert_n(size_type n, const key_type& x);

要素 x を n 個追加します。

// sample
bag<string> b;
b.insert_n(5, "apple"); // "apple"を5個追加
int s = b.size() // s = 5
void insert(const key_type& x);

要素 x を 1 個追加します。

// sample
bag<string> b;
b.insert("apple"); // "apple"を1個追加
int s = b.size() // s = 1
template<class InputIterator>
void insert(InputIterator first, InputIterator last);

シーケンス[first,last)を追加します。

// sample
string s = "hello";
bag<char> b;
b.insert(s.begin(), s.end()); // b = { e h l l o }

erase / clear / swap

void erase(iterator position);

position が指す要素を削除します。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.erase(b.begin()); // 最初の要素を削除
int s = b.size(); // s = 2
size_type erase_all(const key_type& x);

x と等しい要素をすべて削除します。 削除した数を返します。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.erase_all("apple"); // "apple"をすべて削除
int n = b.count("apple"); // n = 0
size_type erase_n(size_type n, const key_type& x);

x と等しい要素を最大 n 個削除します。 削除した数を返します。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.erase_n(2,"apple"); // "apple"を2個削除
int n = b.count("apple"); // n = 1
size_type erase(const key_type& x);

x と等しい要素を最大 1 個削除します。 削除した数を返します。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.erase("apple"); // "apple"を1個削除
int n = b.count("apple"); // n = 2
void erase(iterator first, iterator last);

シーケンス[first,last)を削除します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert_n(2,"banana");
b.insert_n(2,"cherry"); // b = { apple apple banana banana cherry cherry }
bag<string>::iterator f = b.find("banana");
bag<string>::iterator l = b.find("cherry"); ++l; // [f,l) = { banana banana cherry }
b.erase(f,l); // b = { apple apple cherry }
void clear();

全要素を削除します。erase(begin(), end()) と等価です。

// sample
bag<string> b;
b.insert("apple");
b.insert("banana"); // b = { apple banana }
b.clear(); // b = { }
int s = b.size(); // s = 0
void swap(bag& x);

*this と x の全要素を交換します。

// sample
bag<string> b1;
b1.insert_n("apple"); // b1 = { apple apple }
bag<string> b2;
b2.insert_n("banana"); // b2 = { banana banana }
b1.swap(b2); // b1 = { banana banana } , b2 = { apple apple }

replace / unique / unique_copy

bool replace(const key_type& x);

x と等しい要素を x に置き換えます。x と等しい要素がないときは false を返します。

// sample
struct abs_less : binary_function<int,int,bool> {
  // 絶対値で比較
  bool operator()(int x, int y) const
    { return abs(x) < abs(y); }
};

bag<int,abs_less> b;
b.insert(1);
b.insert(2);
b.insert(-2); // b = { 1 2 2 }
b.replace(-2); // b = { 1 -2 -2 }
bool unique(const key_type& x);

x と等しい要素の個数を 1 にします。x と等しい要素がないときは false を返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert("apple");
b.insert("banana"); // b = { apple apple apple banana }
b.unique("apple"); // b = { apple banana }
int n = b.count("apple"); // n = 1
void unique();

全要素の個数を 1 にします。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.insert("banana"); // b = { apple apple apple banana }
b.unique(); // b = { apple banana }
int s = b.size(); // s = 2
template<class OutputIterator>
OutputIterator unique_copy(OutputIterator result) const;

unique()の結果が [result,RET) にコピーされます(*thisの内容は変化しません)。RETはこの関数の戻り値です。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.insert("banana"); // b = { apple apple apple banana }
vector<string> v;
b.unique_copy(back_inserter(v)); // v = { apple banana }

count / entries / find /lower_bound / upper_bound / equal_range

size_type count(const key_type& x) const;

x と等しい要素の個数を返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert("apple");
b.insert("banana"); // b = { apple apple apple banana }
int n = b.count("apple"); // n = 3
size_type entries() const;

互いに異なる要素の個数を返します。

// sample
bag<string> b;
b.insert_n(3,"apple");
b.insert("banana"); // b = { apple apple apple banana }
int n = b.entries(); // n = 2
iterator find(const key_type& x) const;

x と等しい要素を指すイテレータを返します。見つからないときはend()を返します。

// sample
bag<string> b;
b.insert("apple");
b.insert("apple");
b.insert("banana"); // b = { apple apple banana }
bag<string>::iterator i = b.find("apple"); // *i = apple
if ( i != b.end() ) cout << *i << endl;
iterator lower_bound(const key_type& x) const;

x より小さくない最初の要素を指すイテレータを返します。

iterator upper_bound(const key_type& x) const;

x より大きい最初の要素を指すイテレータを返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert_n(2,"banana");
b.insert_n(2,"cherry"); // b = { apple apple banana banana cherry cherry }
bag<string>::iterator l = b.lower_bound("banana"); // *l = banana
bag<string>::iterator u = b.upper_bound("banana"); // *u = cherry
// [l,u) = { banana banana }
std::pair<iterator,iterator>
equal_range(const key_type& x) const;

lower_bound(x), upper_boud(x)の組を返します。

// sample
bag<string> b;
b.insert_n(2,"apple");
b.insert_n(2,"banana");
b.insert_n(2,"cherry"); // b = { apple apple banana banana cherry cherry }
std::pair<bag<string>::iterator,bag<string>::iterator> r
  = b.equal_range("banana");
// [r.first, r.second) = { banana banana }

ソースコード

#ifndef __STX_BAG_H__
#define __STX_BAG_H__

/*
 * 'bag' class template
 *
 * Microsoft Visual C++ v6.0
 * でのコンパイル/実行を確認した。
 *
 *          επιστημη
 */

#include <set>

namespace stx {

  template<class Key, class Compare=std::less<Key>, class Allocator=std::allocator<std::pair<Key,size_t> > >
  class bag {

  public:

    typedef Key                               key_type;
    typedef key_type                          value_type;

  private:

    /* bag<Key> はその内部に set< pair<Key,size_t> > を内包することで要素とその個数を管理する。
     * このとき、set< pair<Key,size_t> > に与える比較オブジェクトは
     * pair<Key,size_t>.first すなわち Key を比較対象としなければならない。
     */
    struct bag_compare {

      Compare comp;

      bag_compare(Compare c)
        : comp(c) {}

      typename Compare::result_type operator()(const std::pair<Key,size_t>& x, const std::pair<Key,size_t>& y) const
        { return comp(x.first, y.first); }

    };

  public:

    typedef std::set<std::pair<Key,size_t>, bag_compare, Allocator> container;
    typedef container::difference_type        difference_type;

    // const_iterator

    class const_iterator : public std::iterator<std::bidirectional_iterator_tag, key_type,difference_type> {

      friend bag;
      container::const_iterator i;
      size_t              c;

      explicit const_iterator(container::const_iterator ci, size_t cc)
        : i(ci), c(cc) {}

    public:

      const_iterator()
        {}

      const_iterator(const const_iterator& it)
        : i(it.i), c(it.c) {}

      const value_type& operator*() const
        { return i->first; }

      const value_type* operator->() const
        { return &(i->first); }

      const_iterator& operator++()
        { if ( i->second <= c ) { ++i; c = 1; } else ++c; return *this; }

      const_iterator operator++(int)
        { const_iterator tmp(*this); ++(*this); return tmp; }

      const_iterator& operator--()
        { if ( c <= 1 ) { --i; c = i->second; } else --c; return *this; }

      const_iterator operator--(int)
        { const_iterator tmp(*this); --(*this); return tmp; }

      bool operator==(const const_iterator& x) const
        { return i == x.i && c == x.c; }

      bool operator!=(const const_iterator& x) const
        { return i != x.i || c != x.c; }

    };

    // const_reverse_iteartor

    class const_reverse_iterator : std::iterator<std::bidirectional_iterator_tag,key_type,difference_type> {

      friend bag;
      container::const_iterator i;
      size_t c;

      explicit const_reverse_iterator(container::const_iterator ci, size_t cc)
        : i(ci), c(cc) {}

    public:

      const_reverse_iterator()
        {}

      const_reverse_iterator(const const_reverse_iterator& it)
        : i(it.i), c(it.c) {}

      const value_type& operator*()
        { const_reverse_iterator tmp(*this); ++tmp; return tmp.i->first; }

      const value_type* operator->()
        { const_reverse_iterator tmp(*this); ++tmp; return &tmp.i->first; }

      const_reverse_iterator& operator++()
        { if ( c <= 1 ) { --i; c = i->second; } else --c; return *this; }

      const_reverse_iterator operator++(int)
        { const_reverse_iterator tmp(*this); ++(*this); return tmp; }

      const_reverse_iterator& operator--()
        { if ( i->second <= c ) { ++i; c = 1 } else ++c; return *this; }

      const_reverse_iterator operator--(int)
        { const_reverse_iterator tmp(*this); --(*this); return tmp; }

      bool operator==(const const_reverse_iterator& x) const
        { return i == x.i && c == x.c; }

      bool operator!=(const const_reverse_iterator& x) const
        { return i != x.i || c != x.c; }

    };

    // iterator / reverse_iterator

    /* イテレータを介してbag内の要素を書き換えることは(要素の大小関係を狂わせるので)許されない。
     * したがって、const_iterator / reverse_const_iterator のみを定義し、
     * iterator / reverse_iterator は それぞれ
     * const_iterator / reverse_const_iterator の typedef である。
     */
    typedef const_iterator                          iterator;
    typedef const_reverse_iterator                  reverse_iterator;

    // types:

    typedef container::key_compare            key_compare;
    typedef container::value_compare          value_compare;
    typedef container::allocator_type         allocator_type;
    typedef container::reference              reference;
    typedef container::const_reference        const_reference;
    typedef container::size_type              size_type;

    // constructor/copy/destroy:

    explicit bag(const Compare& comp =Compare(), const Allocator& alloc =Allocator())
      : c(value_compare(comp),alloc) {}

    template<class InputIterator>
    bag(InputIterator first, InputIterator last, const Compare& comp=Compare(), const Allocator& alloc =Allocator())
      : c(value_compare(comp),alloc) { for ( ;first != last; ++first ) insert(*first); }

    bag(const bag& x)
      { c = x.c; }

    bag& operator=(const bag& x )
      { c = x.c; return *this; }

    ~bag()
      {}

    allocator_type get_allocator() const
      { return c.get_allocator(); }

    // iterators:

    iterator begin()
      { return iterator(c.begin(),1); }

    const_iterator begin() const
      { return const_iterator(c.begin(),1); }

    reverse_iterator rbegin()
      { return reverse_iterator(c.end(),1); }

    const_reverse_iterator rbegin() const
      { return const_reverse_iterator(c.end(),1); }

    iterator end()
      { return iterator(c.end(),1); }

    const_iterator end() const
      { return const_iterator(c.end(),1); }

    reverse_iterator rend()
      { return reverse_iterator(c.begin(),1); }

    const_reverse_iterator rend() const
      { return const_reverse_iterator(c.end(),1); }

    // capacity:

    bool empty() const
      { return c.empty(); }

    /* 以下の実装では、size() の時間計算量が O(N)となり、望ましくない...
     */
    size_type size() const
      { size_type n = 0;
        for ( container::iterator it = c.begin(); it != c.end(); ++it )
          n += it->second;
        return n;
      }

    size_type max_size() const
      { return c.max_size(); }

    // insert:

    void insert_n(iterator position, size_type n, const key_type& x)
      { if ( n == 0 ) return;
	    container::key_type tmp(x,n);
        container::iterator it = c.find(tmp);
        if ( it == c.end() ) c.insert(position.i, tmp);
        else const_cast<size_t&>(it->second) += n;
      }

    void insert(iterator position, const key_type& x)
      { insert_n(position, 1, x); }

    void insert_n(size_type n, const key_type& x)
      { insert_n(begin(), n, x); }

    void insert(const key_type& x)
      { insert_n(begin(), 1, x); }

    template<class InputIterator>
    void insert(InputIterator first, InputIterator last)
      { for ( ;first != last; ++first ) insert(*first); }

    // erase:

/*  stdc++ では set::erase(iterator) の戻り値は void であるため、
 *  以下の実装は誤りである...
 *
 *  iterator erase(iterator position)
 *    { container::iterator it = position.i;
 *      if ( it->second <= 1 ) { it = c.erase(it); return iterator(it,1); }
 *      else { --const_cast<size_t&>(it->second); ++position; }
 *      return position;
 *    }
 */
    void erase(iterator position)
      { container::iterator it = position.i;
        if ( it->second <= 1 ) c.erase(it);
        else --const_cast<size_t&>(it->second);
      }

    size_type erase_all(const key_type& x)
      { size_type n = 0;
        container::iterator it = c.find(container::value_type(x,1));
        if ( it != c.end() ) { n = it->second; c.erase(it); }
        return n;
      }

    size_type erase_n(size_type n, const key_type& x)
      { if ( n == 0 ) return 0;
	    container::iterator it = c.find(container::value_type(x,1));
        if ( it != c.end() ) {
          size_type t = it->second;
          if ( t <= n ) { c.erase(it); n = t; }
          else const_cast<size_t&>(it->second) -= n;
        }
        return n;
      }

    size_type erase(const key_type& x)
      { return erase_n(1,x); }

/*  stdc++ では set::erase(iterator) の戻り値は void であるため、
 *  以下の実装は誤りである...
 *
 *  void erase(iterator first, iterator last)
 *    { while ( first != last ) first = erase(first); }
 */

    void erase(iterator first, iterator last)
      { container tmp(c.key_comp(), c.get_allocator());
        container::iterator i;
        for( ; first != last; ++first ) {
          container::value_type v(*first,1);
          i = tmp.find(v);
          if ( i == tmp.end() ) tmp.insert(v);
          else ++const_cast<size_t&>(i->second);
        }
        for ( i = tmp.begin(); i != tmp.end(); ++i )
          erase(i->second, i->first);
      }

    // other modifiers:

    void swap(bag& x)
      { c.swap(x.c); }

    void clear()
      { c.clear(); }

    // observers:

    key_compare key_comp() const
      { return c.key_comp(); }

    value_compare value_comp() const
      { return c.value_comp(); }

    // bag operations:

    bool replace(const key_type& x)
      { container::iterator it = c.find(container::key_type(x,1));
        if ( it != c.end() ) const_cast<key_type&>(it->first) = x;
        return it != c.end();
      }

    void unique()
      { for ( container::iterator it = c.begin(); it != c.end(); ++it )
          const_cast<size_t&>(it->second) = 1;
      }

    template<class OutputIterator>
    OutputIterator unique_copy(OutputIterator result) const
      { for ( container::const_iterator it = c.begin(); it != c.end(); ++it ) {
          *result = it->first;
          ++result;
        }
        return result;
      }

    size_type count(const key_type& x) const
      { container::const_iterator it = c.find(container::value_type(x,1));
        return it == c.end() ? 0 : it->second;
      }

    size_type entries() const
      { return c.size(); }

    iterator find(const key_type& x) const
      { return iterator(c.find(container::value_type(x,1)),1); }

    iterator lower_bound(const key_type& x) const
      { return iterator(c.lower_bound(container::value_type(x,1)),1); }

    iterator upper_bound(const key_type& x) const
      { return iterator(c.upper_bound(container::value_type(x,1)),1); }

    std::pair<iterator,iterator>
    equal_range(const key_type& x) const
      { std::pair<container::iterator,container::iterator> r = c.equal_range(container::value_type(x,1));
        return std::pair<iterator,iterator>(iterator(r.first,1),iterator(r.second,1));
      }

    // rel-ops:

    friend bool operator==(const bag& x, const bag& y)
      { return x.c == y.c; }

    friend bool operator!=(const bag& x, const bag& y)
      { return x.c != y.c; }

    friend bool operator<(const bag& x, const bag& y)
      { return x.c < y.c; }

    friend bool operator<=(const bag& x, const bag& y)
      { return x.c <= y.c; }

    friend bool operator>(const bag& x, const bag& y)
      { return x.c > y.c; }

    friend bool operator>=(const bag& x, const bag& y)
      { return x.c >= y.c; }

    // swap:

    friend void swap(bag& x, bag& y)
      { x.c.swap(y.c); }

  protected:

    container c;

  };

}

#endif