今日はunderscore的な書き方が好きな人のためのSTLをまとめてみました。
STLは大きく分けて次のようなのがあります。
- コンテナ。 vector list map...
- マルチスレッド thread C++11のみ
- I/O iostream fstream...
- C言語 Cのヘッダいろいろ
- アルゴリズム algorithm 今日の目玉
- そのほか(正規表現 例外 時間系 乱数系...etc)
公式のリファレンス見るとアルゴリズムもなんだその他に入っていますが、これが一番良く使いそう、メタ関数を除くと一番関数があるっていうことで別枠で見た方が良さそうです。
最後の細かい奴は必要であればリファレンスを見てつかうっていうスタンスでいいかと思います。
underscore.js
javascriptにはunderscore.js lodash.jsとかいう超便利関数型っぽいライブラリがありますが、このコーディングスタイルが好きな人で最近C++始めたって人はこのunderscore.js的な書き方がしたいと思ってそう。僕もそう。
http://underscorejs.org/
https://lodash.com/docs
STL
C++の機能に初めから備わっているライブラリです。標準テンプレライブラリ。
underscoreのようなSTLの使い方
早速使い方を見てみましょう
for_each (underscore の each)
# include <algorithm>
# include <vector>
int main() {
//偶数だけをフィルタリング
std::vector<int> num = {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int> even;
std::for_each(num.begin(), num.end(), [&](const int i){
if (i % 2 == 0) {
even.push_back(i);
}
});
}
transform (underscore の map)
# include <algorithm>
# include <vector>
int main() {
std::vector<int> a = {1, 2, 3, 4, 5};
std::vector<int> b;
b.resize(a.size()); //これやらないと壊れる
std::transform(a.begin(), a.end(), b.begin(), [](const int i){
return i * i;
});
// b = {1, 4, 9, 16, 25};
}
accumulate (underscore の reduce)
# include <numeric> //algorithmじゃなくてnumericに入ってるのに注意
# include <vector>
int main() {
std::vector<std::string> a = {"this", "is", "an", "apple."};
std::string b = std::accumulate(a.begin(), a.end(), std::string(), [](const std::string res, const std::string n){
//resは前のイテレーションの結果 "this is an" など
return res + " " + n;
});
//b -> "this is an apple."
}
上記 mapとreduceを組み合わせて文章の単語を数えるロジックがすぐにでも組めそうですね。
集合演算 (underscoreの union, intersection, difference, xor)
set_union set_difference set_intersection set_symmetric_differenceっていうのがそれぞれunderscoreの集合演算に対応しています。
# include <algorithm>
# include <vector>
int main() {
std::vector<int> a = {1, 2, 3, 4, 5};
std::vector<int> b = {1, 3, 5, 7, 9};
std::vector<int> c(a.size()); //メモリ領域を確保しておく
//ソート済みとしておく
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
//集合演算ではイテレータが返却される
auto u_it = set_union(a.begin, a.end(), b.begin(), b.end(), c.begin());
//auto i_it = set_intersection(a.begin, a.end(), b.begin(), b.end(), c.begin());
//auto d_it = set_difference(a.begin, a.end(), b.begin(), b.end(), c.begin());
//auto s_it = set_symmetric_difference(a.begin, a.end(), b.begin(), b.end(), c.begin());
//サイズを戻す
c.resize(u_it - c.begin()); // c -> {1, 2, 3, 4, 5, 7, 9}
}
これはあまり使いやすくないかもしれませんね。
unique (underscore の uniq)
# include <algorithm>
# include <vector>
int main() {
std::vector<int> a = {1, 2, 4, 5, 2, 3, 5};
//ソートしてないと無意味
std::sort(a.begin(), a.end());
auto it = std::unique(a.begin(), a.end());
//サイズ変更
a.resize(it - a.begin()); // a-> {1, 2, 3, 4, 5};
}
count_if (underscore の countBy)
# include <algorithm>
# include <vector>
int main() {
vector<int> a = {1, 2, 3, 4, 5, 6};
const auto even = std::count_if(a.begin(), b.end(), [](const int i){
return i % 2 == 0;
});
// even -> 3
}
などなど、他にも filterに相当するものや、findに相当するものがあります。
これらを基本形としてSTLでコードを記述する習慣をつけるとC++らしいいいコードが出来上がること間違い無しですね。
なお、for_eachはそれっぽいのがすでに組み込みにありますが、なんとなく他のコレクション操作の対称性とか考えるとこっちのSTLの方が気持ち良い気がしています。
underscoreをマスターすることでjavascriptでの実装に何か天啓を得た人もいることでしょうが、C++ではSTLがその天啓にあたるかもしてません。