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

## 概要

Rubyのは配列は便利なメソッドがたくさんある。（Enumerableモジュール）
RubyのEnumerableでできることが、C++ではどのように書くことができるかまとめておく。

- 前提条件
  - C++11
  - コンテナはvector（mapやlistについては別エントリで）

## メソッド


### each

要素に対してループを回す

- ruby

```rb
[1,2,3,4,5].each do |x|
  print "#{2*x},"    #=> 2,4,6,8,10,
end
```

- c++
  - c++11から範囲に基づくforループが使える。c++03に比べてだいぶ簡潔に書けるようになった。

```cpp
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

```rb
[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を取りたい場合はループ変数を使う

```cpp
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
  - 最初または最後の要素を取得したい場合

```rb
a = [1,2,3]
a.first  #=> 1
a[0]     #=> 1
a.last   #=> 3
a[-1]    #=> 3
```

- c++
  - indexに負の数は使えない

```cpp
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` だと破壊的な操作, `+`だと非破壊的操作

```rb
a1 = [1,2,3]
a2 = [4,5]
a1.concat(a2)
# a1 => [1,2,3,4,5], a2 => [4,5]
```

- c++
  - v1が変更される（破壊的操作）。v2の要素がコピーされる。O(N)

```cpp
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

```rb
a = [1,2]
a << 3
# a => [1,2,3]
```

- cpp

```cpp
std::vector<long> a = {1,2};
a.push_back(3);
// a => [1,2,3]
```

### pop

- ruby
  - 末尾の要素を削除し、その値を返す

```rb
a = [1,2,3]
a.pop    #=> 3
a        #=> [1,2]
```

- cpp
  - 末尾の要素を参照するためには`back()`または`[v.size()-1]`を使い、その要素を削除するには`pop_back()`を使う。
  - `pop_back()`の返り値はvoidなので注意。

```cpp
std::vector<long> a = {1,2,3};
a[ a.size()-1 ];                 // => 3
a.pop_back();                    // => 返り値はvoid。メソッドの実行後のaは`[1,2]`となる。
```

### map

- ruby

```rb
[1,2,3].map! {|x| 2*x }  #=> [2,4,6]
```

- c++

```cpp
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が返る

```rb
[7,8,9,10,11].find {|x| x%5 == 0 }  #=> 10
```

- c++
  - 単純に`==`で比較する場合には、`std::find`を使えば良い。O(N)の計算量。
  - `std::find_if` を使う。`#include <algorithm>` が必要。
  - 要素が見つからない場合は `v.end()` が返る

```cpp
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
```

```cpp
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) で探せる。

```cpp
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

```rb
[1,2,3,4,5].find_all {|x| x>3 }  #=> [4,5]
```

- c++

```cpp
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

```rb
[4,1,3,2,5].sort!  #=> [1,2,3,4,5]
```

- c++

```cpp
std::vector<long> v = {4,1,3,2,5};
std::sort( v.begin(), v.end() );
// v => [1,2,3,4,5]
```

### sort_by

- ruby

```rb
[4,1,2,3,5].sort_by! {|x| (3-x).abs }  #=> [3,4,2,1,5]
```

- cpp
  - `std::sort`を使う。`#include <algorithm>` が必要
  - 第３引数には "less than" を定義するファンクタを渡す

```cpp
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

```rb
[5,1,4,2,3].index(2)  #=> 3
```

- c++
  - `find_if` でiteratorを取得して、先頭からの距離を計算するとindexになる。
  - 見つからない場合は、`v.size()`と等しくなる。

```cpp
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

```rb
[2,4,6,8].all? {|x| x % 2 == 0 }  #=> true
[2,4,6,9].all? {|x| x % 2 == 0 }  #=> false
```

- c++

```cpp
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を使う

```rb
[1,3,5].inject(:+)   #=> 9
```

- c++
  - accumulate を使うと簡単
  - デフォルトで足し算を行う
  - `#include <numeric>` が必要

```cpp
std::vector<long> v = {1,3,5};
long sum = std::accumulate(v.begin(), v.end(), 0);  // => 9
```

### inject (reduce)

- ruby

```rb
[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++

```cpp
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

```rb
[5,1,1,2,3,5,5].uniq!  #=> [5,1,2,3]
```

- c++
  - setを作ってinsertしていく。insertに成功した場合は配列に残すが、失敗した場合は配列から除く
  - (注意) `std::unique` は配列の**連続する**重複要素を取り除くためにしか使えない。
      - 事前にソートしてある配列には使える

```cpp
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++ (ソート済みの場合)

```cpp
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
  - ブロックを評価した結果が重複しているものを削除する

```rb
[4,5,1,2,3,4,5].uniq! {|x| x % 3 }   #=> [4,5,3]
```

- c++

```cpp
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

```rb
[1,5,3,2].join(":")  #=> "1:5:3:2"
```

- c++
  - `ostringstream` を使うには `#include <sstream>` が必要

```cpp
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

```rb
[1,2,3].reverse!  #=> [3,2,1]
```

- c++
  - `#include <algorithm>` が必要

```cpp
std::vector<long> v = {1,2,3};
std::reverse( v.begin(), v.end() );
// v => [3,2,1] 
```

### group_by

- ruby

```rb
["cat","bat","bear","camel","alpaca"].group_by {|x| x[0]}
# {"c"=>["cat", "camel"], "b"=>["bat", "bear"], "a"=>["alpaca"]}
```

- c++
  - 結果はmapに格納する。`#include <map>` が必要

```cpp
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

```rb
[1,2,3,4,5,6,7].shuffle   #=> [2, 6, 4, 7, 5, 3, 1]
```

- c++
  - `#include <random>` が必要

```cpp
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

```rb
[1,2,3].inspect  # "[1, 2, 3]"
```

- C++

```cpp
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]
```