buq’s blog

覚えておきたいけど覚えておけなさそうなことを書きます?

関数オブジェクト @ C++

C++には関数オブジェクトというものがある. class に operator()を適当に定義してやれば作ることができ, これを用いるとパラメタつき関数みたいなものが実現できる.

ここではパラメタつき比較関数(二つの引数をとって,前者が後者より厳密に小さいときtrueを返す )を実装している.この比較関数は int 型の「大小」を比較するもので, パラメタeven_regarded_smallertrueであれば,偶数は奇数より小さいと判断され,偶奇が一致するものは普通の大小関係で比較される.false であれば奇数が偶数より小さいと判断され,偶奇が一致するものは普通の大小関係で比較される.

下記のコードはC++11で書かれているので, 古いコンパイラで怒られるようであれば適当な書き換えが必要.

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

#define say(smth) std::cout << smth << std::endl

using namespace std;

class ParityFirstComp{
  //static   
public:
  static bool is_even(const int& n) {return n%2==0;}
  static bool is_odd (const int& n) {return n%2!=0;} //n%2 can be -1
  static bool have_same_parity(const int& m, const int& n){return (n-m)%2==0;}

  //non-static
private:  
  bool even_regarded_smaller;
public:
  ParityFirstComp(bool even_regarded_smaller):
    even_regarded_smaller(even_regarded_smaller)
  {}
  
  bool compare_by_parity(const int &l, const int& r) const{
    if(even_regarded_smaller)
      return is_even(l) && is_odd (r);
    else
      return is_odd (l) && is_even(r);
  }
  bool operator()(const int &l, const int& r) const{
    if(have_same_parity(l, r))
      return l < r;
    else
      return compare_by_parity(l, r);
  }
};

template<class T>
ostream& operator<< (ostream& strm, const vector<T>& v){
  for(auto&& elm : v) strm << elm << ", ";
  return strm;
}

int main(){
  vector<int> v(10);
  iota(v.begin(), v.end(), -4);
  random_shuffle(v.begin(), v.end());
  vector<int> u(v);

  say("original:");
  say(v);

  say("even_regarded_smaller:");
  ParityFirstComp even_first_comp(true);
  sort(v.begin(), v.end(), even_first_comp);
  say(v);

  say("odd_regarded_smaller:");
  ParityFirstComp odd_first_comp(false);
  sort(u.begin(), u.end(), odd_first_comp);
  say(u);

  return 0;
}

出力は次のようになる.(未ソートの結果はrandom_shuffleの実装に依存すると思う)

original:
6, 0, 3, 5, 7, 8, 4, 1, 2, 9, 
even_regarded_smaller:
0, 2, 4, 6, 8, 1, 3, 5, 7, 9, 
odd_regarded_smaller:
1, 3, 5, 7, 9, 0, 2, 4, 6, 8, 

それっぽい挙動をしている.

2015/8/23 負の整数にも対応できるよう is_odd を書き換えた.