LoginSignup
17
14

More than 3 years have passed since last update.

Ruby-C++ 配列操作の対応まとめ(vector編)

Last updated at Posted at 2015-01-08

概要

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> が必要
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]
17
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14