Upload
yak1ex
View
5.745
Download
2
Embed Size (px)
Citation preview
自己紹介
氏名: 新康孝 (あたらし やすたか)
Twitter ID: yak_ex
Web: http://yak3.myhome.cx:8080/junks
C++ / Perl が主戦場
現在、仕事でコードに触れていないので競技プログラミング(TopCoder、Codeforces)で潤い補充
闇の軍団に憧れるただの C++ 好き
C++0x とは
国際標準化機構 ISO で規格化されている C++ 規格(ISO 14882)の次期版仮称 200x 年代に発行予定だったが間に合わなかったため 0x は 16 進プレフィクスということにhttp://www2.research.att.com/~bs/C++0xFAQ.html
先頃 Final Draft が承認され規格発行を待つ状態であり正式名称 14882:2011 となる予定
小修正だった 14882:2003 とは異なり、言語自体また標準ライブラリに非常に大きな変更が加えられている
本稿では競技プログラミングから見た C++0x の利点について紹介する Codeforces で gcc 4.6 での C++0x 機能が使えるのでそれを基準として説明する
目次
auto
Range-based for
Lambda
tuple
initializer_list
unordered_map / unordered_set
新規アルゴリズム (iota, all_of)
ちょっとした改善 (map::at)
対応微妙(regex / emplace)
auto
例えば set の要素についてループする場合、Iterator の型を書くのが面倒
set<int> s;
for(set<int>::iterator it = s.begin(); it != s.end(); ++it) {
if(*it == 5) it = s.erase(it);
}
set<int> s;
for(auto it = s.begin(); it != s.end(); ++it) {
if(*it == 5) it = s.erase(it);
}
→auto にしておけばコンパイラが型を導出してくれる
Range-based for ループですっきり
コンテナ s の各要素を v に割り当ててループ
Range-based for
例えば単純に set の要素についてループする場合、C++03では色々と面倒
std::set<int> s;
for(std::set<int>::iterator it = s.begin(); it != s.end(); ++it)
std::cout << *it << std::endl;
std::set<int> s;
for(auto it = s.begin(); it != s.end(); ++it)
std::cout << *it << std::endl;
auto でもやっぱり微妙
set<int> s;
for(auto v : s)
std::cout << v << std::endl;
lambda (無名関数オブジェクト)
<algorithm>は便利だけど関数オブジェクト書くのが面倒でループで書いてしまう
// 関数オブジェクトstruct check2nd
{
check2nd(const int &n) : n(n) {}
bool operator()(const std::pair<int, int> &p) { return p.second == n; }
const int &n;
};
std::vector<std::pair<int, int>> v; int k;
return std::count_if(v.begin(), v.end(), check2nd(k));
// 自前ループstd::vector<std::pair<int, int>> v; int k;
int result = 0;
for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result;
return result;
lambda (無名関数オブジェクト)
<algorithm>は便利だけど関数オブジェクト書くのが面倒でループで書いてしまう
↓
その場で関数オブジェクトが書けるスコープ内の変数を参照可能
// lambda
vector<std::pair<int, int>> v; int k;
return std::count_if(v.begin(), v.end(),
[&](const std::pair<int, int>& p) { return p.second == k; });
中身が return だけなら戻り値の型を省略可能[&](const std::pair<int, int>& p) -> bool { return p.second == k; }
[&] はスコープ内変数の参照の仕方の指定
lambda (関数内関数風)
ヘルパ関数として切り出したいけど状態渡すのが面倒int count2nd(const std::vector<std::pair<int, int>> &v, int k) {
int result = 0;
for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result;
return result;
}
std::vector<std::pair<int, int>> v;
if(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */
マクロは C++er として認められないしグローバル変数は未初期化とか上書きの問題がある
lambda (関数内関数風)
ヘルパ関数として切り出したいけど状態渡すのが面倒int count2nd(const std::vector<std::pair<int, int>> &v, int k) {
int result = 0;
for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result;
return result;
}
std::vector<std::pair<int, int>> v;
if(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */
std::vector<std::pair<int, int>> v;
auto count2nd = [&] (int k) -> int {
int result = 0;
for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result;
return result;
};
if(count2nd(0) == 0 && count2nd(1) == v.size()) /* 適当 */
関数内関数的にその場でヘルパ関数を定義できる
lambda (関数内関数風)
ヘルパ関数として切り出したいけど状態渡すのが面倒int count2nd(const std::vector<std::pair<int, int>> &v, int k) {
int result = 0;
for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result;
return result;
}
std::vector<std::pair<int, int>> v;
If(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */
std::vector<std::pair<int, int>> v;
auto count2nd = [&] (int k) {
return count_if(v.begin(), v.end(),
[&] (const std::pair<int, int>&p) { return p.second == k; });
};
if(count2nd(0) == 0 && count2nd(1) == v.size()) /* 適当 */
関数内関数的にその場でヘルパ関数を定義できる※内部でも lambda を使った場合
lambda (関数内関数風)
応用編再帰関数を定義したい→ std::function と組み合わせ
std::vector<std::vector<int>> adj; int goal;
std::vector<bool> visited(adj.size());
// auto dfs = … だとエラーが出るstd::function<bool(int)> dfs = [&](int n) -> bool {
if(n == goal) return true;
visited[n] = true;
for(int i: adj[n]) {
if(!visited[i]) {
bool res = dfs(i);
if(res) return true;
}
}
return false;
};
※std::funciton は関数オブジェクトも持てる関数ポインタ的イメージ
tuple
いちいち構造体を宣言し、かつ比較演算子を用意するのがかったるい
struct data { int level, value, cost; };
bool operator<(const data& d1, const data &d2){
return d1.level < d2.level
|| d1.level == d2.level && d1.value < d2.value
|| d1.level == d2.level && d1.value == d2.value && d1.cost < d2.cost;
}
std::set<data> s;
typedef std::tuple<int, int, int> data; // 辞書式比較演算子定義済みstd::set<data> s;
// こういう用意をしておくてアクセスが分かりやすいかもenum { LEVEL, VALUE, COST };
std::get<LEVEL>(*s.begin());
ref. http://topcoder.g.hatena.ne.jp/cafelier/20110816/1313498443
tuple にお任せ
initializer_list
vector とかの内容を自由に初期化する方法がない
std::vector<int> v(5);
int init[5] = { 1, 3, 2, 5, 4 };
std::copy(init, init + 5, v.begin());
std::vector<int> v({1, 3, 2, 5, 4});
// コンストラクタ呼び出しは () じゃなくて {} でもできるようになったので// 以下も OK
std::vector<int> v{1, 3, 2, 5, 4};
→初期化リスト(initializer_list)をとるコンストラクタを定義できる
map だって初期化できる
std::map<std::string, int> v = {
{ "first", 1 },
{ "second", 2 }
};
initializer_list
応用編min / max
int minimum = std::min(std::min(a, b), std::min(c, d));
int minimum = std::min({a, b, c, d});
min / max は 2 引数だったので複数値の min / max
をとりたい場合は多段で適用する必要があった
→initializer_list を受けられるようになったので一発で OK
initializer_list
応用編固定値のリストでループ
int init[] = { 1, 2, 3, 5, 8, 13, 21 };
for(std::size_t i = 0; i < sizeof(init) / sizeof(init[0]); ++i) {
std::cout << init[i] << std::endl;
}
for(int i : { 1, 2, 3, 5, 8, 13, 21} ) {
std::cout << i << std::endl;
}
固定値を持つ配列を用意して添え字でループ
→Range-based for と initializer_list の組み合わせでこんな簡潔に
unordered_map / unordered_set
map / set はあるけど計算量が O(log n)
std::set<int> s;
s.insert(5); // 挿入 O(log n)
s.erase(5); // 削除 O(log n)
s.count(3); // 検索 O(log n)
std::unordered_set<int> s;
s.insert(5); // 挿入 O(1)
s.erase(5); // 削除 O(1)
s.count(3); // 検索 O(1)
// トレードオフはメモリ使用量
→いわゆるハッシュテーブルとしてunordered_map / unordered_set が導入※hash_map / hash_set だと既存実装とぶつかるので別名で導入
新規アルゴリズム(iota)
インデックス用配列の初期化めんどいstd::vector<int> vdata;
std::vector<int> index(vdata.size());
for(std::size_t i = 0; i < index.size(); ++i) index[i] = i;
std::sort(index.begin(), index.end(),
[&](int n1, int n2) { return vdata[n1] < vdata[n2]; });
for(int i: index) { std::cout << i << " : " << vdata[i] << std::endl; }
std::vector<int> vdata;
std::vector<int> index(vdata.size());
std::iota(index.begin(), index.end(), 0); // 0 が初期値で 0, 1, 2, …
std::sort(index.begin(), index.end(),
[&](int n1, int n2) { return vdata[n1] < vdata[n2]; });
for(int i: index) { std::cout << i << " : " << vdata[i] << std::endl; }
→地味に仕事をする std::iota
#define RNG(c) (c).begin(), (c).end() でさらに便利
新規アルゴリズム(*_of)
→all_of / any_of / none_of
名前通り
std::vector<int> v;
if(std::all_of(v.begin(), v.end(), [](int n){ return n >= 5; })) {
/* 全要素 5 以上の場合 */
}
if(std::any_of(v.begin(), v.end(), [](int n){ return n >= 5; })) {
/* どれか1要素でも 5 以上の場合 */
}
if(std::none_of(v.begin(), v.end(), [](int n){ return n >= 5; })) {
/* 5 以上の要素がない場合 */
}
ちょっとした改善(map::at)
map::operator[] のせいで const 性の維持が面倒
int get(const std::map<std::string, int> &m, const std::string &s)
{
// map::operator[] に const 版はないのでこう書けない// return m[s];
return m.find(s)->second;
}
int get(const std::map<std::string, int> &m, const std::string &s)
{
return m.at(s);
}
→const 版のある at() で OK
対応微妙(regex, emplace)
→regex(正規表現)で文字列処理が容易に!※でも gcc(libstdc++) は対応微妙
std::vector<pair<std::string, int>> v;
v.push_back(std::make_pair(std::string("hoge"), 5));
→emplace で insert 時等の生成を省略※でも gcc(libstdc++) はコンテナ毎に対応微妙
std::vector<pair<std::string, int>> v;
v.emplace_back("hoge", 5);
※直接、挿入する場所に値を生成するイメージ
参考文献
競技プログラマのためのC++0xB
http://topcoder.g.hatena.ne.jp/cafelier/201
10816/1313498443
Wikipedia C++0x
http://ja.wikipedia.org/wiki/C%2B%2B0x
cpprefjp
https://sites.google.com/site/cpprefjp/