概要
Rubyのは配列は便利なメソッドがたくさんある。(Enumerableモジュール)
RubyのEnumerableでできることが、C++ではどのように書くことができるかまとめておく。
- 前提条件
- C++11
- コンテナはvector(mapやlistについては別エントリで)
メソッド
each
要素に対してループを回す
- ruby
[1,2,3,4,5].each do |x|
print "#{2*x}," #=> 2,4,6,8,10,
end
- c++
- c++11から範囲に基づくforループが使える。c++03に比べてだいぶ簡潔に書けるようになった。
std::vector<long> a = {1,2,3,4,5};
for( const auto& x : a ) {
std::cout << 2 * x << ","; // => 2,4,6,8,10
}
each_with_index
indexと一緒にループを回す
- ruby
[5,4,3,2,1].each_with_index do |x,idx|
print "#{idx}:#{x}, "
end
# => 0:5, 1:4, 2:3, 3:2, 4:1,
- c++
- indexを取りたい場合はループ変数を使う
std::vector<long> a = {5,4,3,2,1};
for( size_t i=0; i < a.size(); i++) {
std::cout << i << ":" << a[i] << ", ";
}
// => 0:5, 1:4, 2:3, 3:2, 4:1,
first, last
- ruby
- 最初または最後の要素を取得したい場合
a = [1,2,3]
a.first #=> 1
a[0] #=> 1
a.last #=> 3
a[-1] #=> 3
- c++
- indexに負の数は使えない
std::vector<long> v = {1,2,3};
long& i = v.first(); // => 1。最初の要素の参照を得る。つまりこの変数に代入可能。
v[0]; // => 1
long& j = v.last(); // => 3。最後の要素の参照を得る。
v[v.size()-1]; // => 3
concat
配列の結合
- ruby
-
concat
だと破壊的な操作,+
だと非破壊的操作
-
a1 = [1,2,3]
a2 = [4,5]
a1.concat(a2)
# a1 => [1,2,3,4,5], a2 => [4,5]
- c++
- v1が変更される(破壊的操作)。v2の要素がコピーされる。O(N)
std::vector<long> v1 = {1,2,3};
std::vector<long> v2 = {4,5};
v1.insert( v1.end(), v2.begin(), v2.end() );
// v1 => [1,2,3,4,5] , v2 => [4,5]
push, <<
末尾に要素を追加
- ruby
a = [1,2]
a << 3
# a => [1,2,3]
- cpp
std::vector<long> a = {1,2};
a.push_back(3);
// a => [1,2,3]
pop
- ruby
- 末尾の要素を削除し、その値を返す
a = [1,2,3]
a.pop #=> 3
a #=> [1,2]
- cpp
- 末尾の要素を参照するためには
back()
または[v.size()-1]
を使い、その要素を削除するにはpop_back()
を使う。 -
pop_back()
の返り値はvoidなので注意。
- 末尾の要素を参照するためには
std::vector<long> a = {1,2,3};
a[ a.size()-1 ]; // => 3
a.pop_back(); // => 返り値はvoid。メソッドの実行後のaは`[1,2]`となる。
map
- ruby
[1,2,3].map! {|x| 2*x } #=> [2,4,6]
- c++
std::vector<long> a = {1,2,3};
auto twice = [](long x)->long { return 2*x; };
for( auto& x : a ) { twice(x); }
// a => [2,4,6]
find
- ruby
- 要素が見つからない場合はnilが返る
[7,8,9,10,11].find {|x| x%5 == 0 } #=> 10
- c++
- 単純に
==
で比較する場合には、std::find
を使えば良い。O(N)の計算量。 -
std::find_if
を使う。#include <algorithm>
が必要。 - 要素が見つからない場合は
v.end()
が返る
- 単純に
std::vector<long> v = {7,8,9,10,11};
auto found = std::find(v.begin(), v.end(), 10);
if( found == v.end() ) { std::cout << "Not found" << std::endl; }
else { "Found: " << *found << std::endl; }
// Found: 10
std::vector<long> v = {7,8,9,10,11};
auto f = [](long x)->bool { return (x%5==0); };
auto found = std::find_if( v.begin(), v.end(), f );
if( found == v.end() ) {
std::cout << "Not found" << std::endl;
}
else {
std::cout << "Found: " << *found << std::endl;
}
// Found: 10
- c++(事前に配列がソートしてある場合)
- binary search で O(logN) で探せる。
std::vector<long> v = {7,8,9,10,11};
auto found = std::lower_bound( v.begin(), v.end(), 10 );
if( found == v.end() ) { std::cout << "Not found" << std::endl; }
else { std::cout << "Found: " << *found << std::endl; }
// *found = 10
find_all
- ruby
[1,2,3,4,5].find_all {|x| x>3 } #=> [4,5]
- c++
std::vector<long> a = {1,2,3,4,5};
std::vector<long> result;
auto condition = [](long x)->bool { return (x>3); };
for( const auto& x : a ) {
if( condition(x) ) { result.push_back(x); }
}
// result => [4,5]
sort
- ruby
[4,1,3,2,5].sort! #=> [1,2,3,4,5]
- c++
std::vector<long> v = {4,1,3,2,5};
std::sort( v.begin(), v.end() );
// v => [1,2,3,4,5]
sort_by
- ruby
[4,1,2,3,5].sort_by! {|x| (3-x).abs } #=> [3,4,2,1,5]
- cpp
-
std::sort
を使う。#include <algorithm>
が必要 - 第3引数には "less than" を定義するファンクタを渡す
-
std::vector<long> v = {4,1,2,3,5};
auto f = [](long x)->long { return std::abs(3-x); };
std::sort(
v.begin(),
v.end(),
[&](const long& lhs, const long& rhs)->bool {
return( f(lhs) < f(rhs) );
}
);
// v => [3,4,2,1,5]
index
- ruby
[5,1,4,2,3].index(2) #=> 3
- c++
-
find_if
でiteratorを取得して、先頭からの距離を計算するとindexになる。 - 見つからない場合は、
v.size()
と等しくなる。
-
std::vector<long> v = {5,1,4,2,3};
auto f = [](long x)->bool { return (x == 2); };
auto found = std::find_if(v.begin(), v.end(), f );
size_t dist = std::distance(v.begin(), found);
all?
- ruby
[2,4,6,8].all? {|x| x % 2 == 0 } #=> true
[2,4,6,9].all? {|x| x % 2 == 0 } #=> false
- c++
std::vector<long> v = {2,4,6,8};
bool b1 = std::all_of( v.begin(), v.end(), [](long x) {
return x%2==0;
});
// b1 => true
std::vector<long> v = {2,4,6,9};
bool b2 = std::all_of( v.begin(), v.end(), [](long x) {
return x%2==0;
});
// b2 => false
sum
- ruby
- injectを使う
[1,3,5].inject(:+) #=> 9
- c++
- accumulate を使うと簡単
- デフォルトで足し算を行う
-
#include <numeric>
が必要
std::vector<long> v = {1,3,5};
long sum = std::accumulate(v.begin(), v.end(), 0); // => 9
inject (reduce)
- ruby
[1,2,3,4,5].inject(1) {|acc,x| acc*x } #=> 120
[1,2,3,4,5].inject("") {|acc,x| acc + (x%2==0?'e':'o') } #=> oeoeo
- c++
std::vector<long> v = {1,2,3,4,5};
long prod = std::accumulate(v.begin(), v.end(), 1, [](long acc, long x) {
return acc*x;
});
// prod => 120
std::string str = std::accumulate(v.begin(), v.end(), std::string(), [](const std::string& acc, long x)->std::string {
return acc + ((x%2==0)?"e":"o");
});
// str => "oeoeo"
uniq
- ruby
[5,1,1,2,3,5,5].uniq! #=> [5,1,2,3]
- c++
- setを作ってinsertしていく。insertに成功した場合は配列に残すが、失敗した場合は配列から除く
- (注意)
std::unique
は配列の連続する重複要素を取り除くためにしか使えない。- 事前にソートしてある配列には使える
std::vector<long> v = {5,1,1,2,3,5,5};
std::set<long> tmpset;
auto sorted = v.begin();
for( auto it = v.begin(); it != v.end(); ++it ) {
if( tmpset.insert(*it).second ) {
*sorted = *it;
sorted++;
}
}
v.erase( sorted, v.end() );
// v => [5,1,2,3]
- c++ (ソート済みの場合)
std::vector<long> v = {1,1,2,3,4,4,4};
v.erase( std::unique(v.begin(), v.end()), v.end() );
// v => [1,2,3,4]
uniq { block }
- ruby
- ブロックを評価した結果が重複しているものを削除する
[4,5,1,2,3,4,5].uniq! {|x| x % 3 } #=> [4,5,3]
- c++
std::vector<long> v = {4,5,1,2,3,4,5};
auto f = [](long x)->long { return x % 3; };
std::set<long> tmpset;
auto sorted = v.begin();
for( auto it = v.begin(); it != v.end(); ++it ) {
if( tmpset.insert( f(*it) ).second ) {
*sorted = *it;
++sorted;
}
}
v.erase( sorted, v.end() );
// v => [4,5,3]
join
- ruby
[1,5,3,2].join(":") #=> "1:5:3:2"
- c++
-
ostringstream
を使うには#include <sstream>
が必要
-
std::vector<long> v = {1,5,3,2};
auto io = std::ostringstream();
std::string separator = ":";
for(auto it = v.begin(); it != v.end(); ) {
io << *it;
if( (++it) != v.end() ) { io << separator; }
}
reverse
- ruby
[1,2,3].reverse! #=> [3,2,1]
- c++
-
#include <algorithm>
が必要
-
std::vector<long> v = {1,2,3};
std::reverse( v.begin(), v.end() );
// v => [3,2,1]
group_by
- ruby
["cat","bat","bear","camel","alpaca"].group_by {|x| x[0]}
# {"c"=>["cat", "camel"], "b"=>["bat", "bear"], "a"=>["alpaca"]}
- c++
- 結果はmapに格納する。
#include <map>
が必要
- 結果はmapに格納する。
std::vector<std::string> v = {"cat","bat","bear","camel","alpaca"};
auto f = [](const std::string& s)-> char { return s.c_str()[0]; };
std::map<char, std::vector<std::string> > result;
for( const auto& x : v ) {
result[ f(x) ].push_back(x);
}
// { 'a' => ["alpaca"], 'b' => ["bat", "bear"], 'c' => ["cat", "camel"] }
shuffle
- ruby
[1,2,3,4,5,6,7].shuffle #=> [2, 6, 4, 7, 5, 3, 1]
- c++
-
#include <random>
が必要
-
std::vector<long> v = {1,2,3,4,5,6,7};
std::mt19937 rnd(1234);
std::shuffle(v.begin(), v.end(), rnd);
// v => [4, 7, 2, 1, 5, 3, 6]
inspect
配列の中身の表示(デバッグ用)
- Ruby
[1,2,3].inspect # "[1, 2, 3]"
- C++
template <class T>
std::string Inspect( const std::vector<T>& v ) {
std::ostringstream oss; // #include <sstream> が必要
oss << "[";
for( size_t i=0; i < v.size(); i++) {
oss << v[i];
if( i != v.size() - 1 ) { oss << ", "; }
else { oss << "]" << std::endl; }
}
oss.str();
}
std::vector<long> v = {1,2,3};
Inspect(v); // => [1,2,3]