LoginSignup
3
1

More than 1 year has passed since last update.

PythonistaのためのC++記法入門: それC++ではどう書くの2 ~基本的な配列~

Last updated at Posted at 2021-08-11

PythonistaのためのC++記法入門: それC++ではどう書くの1の続き。今回は配列。

配列

Pythonにおける配列として代表的なのはlistであり、C++でそれに近い挙動をするのはarraylistおよびvectorである。ここでは主にvectorを例に挙げてPythonのlistとの用法を比較していく。

vector

vector末尾への要素追加シーケンス内要素へのランダムアクセスが$O(1)$と高速に行える一方、任意位置への要素の挿入は$O(N)$と要素数に応じた時間がかかる。こういった特性、およびarrayと異なり動的配列をサポートしているなど、Pythonのlistはこちらに近い。

1次元配列

Pythonではこう書いた

data = list()  # 空の配列
data = [0, 0, 0]  # [0, 0, 0] で初期化
data = [128, 128, 128]  # [128, 128, 128] で初期化
data = [1, 2, 3]  # [1, 2, 3] で初期化

val = data[0]  # 0番目の要素がvalに代入される
s = len(data)  # 配列の要素数が出力される
data.append(10)  # 末尾に10が追加される
data.pop()  # 末尾である10が削除されて出力される

C++ではこう書く

vector<int> data;  // 空の配列、ただし要素はintと既定
vector<int> data(3);  // 要素数は3だが中身は未初期化値
vector<int> data(3, 128); // 要素数3かつそれぞれ128で初期化
vector<int> data = {1, 2, 3};  // 3つの要素{1, 2, 3}からなる配列となる
vector<int> data = {1, 2, 3};  // 3つの要素{1, 2, 3}からなる配列を作成
cout << data.at(0) << " " << data.at(1) << " " << data.at(2) << endl;
// "1 2 3"が出力される
int val;
val = data.at(0);  // 0番目の要素がint型の変数valに代入される
val = data[0];  // 変数名[i]でもi番目の要素にアクセスできる

// 要素の変更
data.at(2) = 300;  // data[2] = 300;でも同様
cout << data.at(0) << " " << data.at(1) << " " << data.at(2) << endl;
// "1 2 300"が出力される

data.push_back(10);  // 末尾に10が追加される
cout << data.at(0) << " " << data.at(1) << " " << data.at(2) << " " << data.at(3) << endl;
// "1 2 300 10" が出力される

data.pop_back();  // 末尾である10が削除される
// 削除されたか確認する
int s = data.size();  //  配列の要素数をint型の変数sに代入
cout << s << endl;  // 3 が出力される
for (int i = 0; i < s; i++){
  cout << data.at(i) << " ";
}
cout << endl;
// "1 2 300"が出力される

以下のような書き方もある。

int data[3];  // int型の3要素の配列が作られる。要素は未初期化。int data[3] = {1, 2, 3};などとすれば初期化は可能。

// data[N]で各要素にアクセスが可能
cout << data[0] << " " << data[1] << " " << data[2] << endl;
// "0 4196272 0" と出力される(未初期化値)

// data[N]で各要素の値のセットが可能
data[1] = 120;
cout << data[0] << " " << data[1] << " " << data[2] << endl;  
// 今回は"0 120 0" と出力される

多次元配列

配列の配列、すなわちC++ではvectorを要素に持つvectorであり、Pythonではlistlistである。

C++ではこう書く

// 縦3×横4の配列
vector<vector<int>> data(3, vector<int>(4));
// 要素数が未定の場合
vector<vector<int>> data;

// 配列の配列であるdataに3要素からなる配列を追加
vector<int> tmp = {1, 2, 3};
data.push_back(tmp);  // data = [[1, 2, 3]]

// 異なる要素数の配列を追加できる
vector<int> tmp2 = {10, 20, 30, 40};
data.push_back(tmp2);  // data = [[1, 2, 3], [10, 20, 30, 40]]

まとめ

C++ Python
宣言 / 定義 vector<型> vec; vec = list()
要素を追加 vec.push_back(要素); vec.append(要素)
先頭要素へのアクセス vec.front(); vec[0]
末尾要素へのアクセス vec.back(); vec[-1]
任意要素へのアクセス(1) 1 vec.at(i); vec[i]
任意要素へのアクセス(2) vec[i]; 2 同上
要素を任意位置に挿入 3 vec.insert(位置, 要素); vec.insert(位置, 要素)
末尾要素を削除 vec.pop_back(); _ = vec.pop() 4
任意要素を削除 vec.erase(位置); 5 vec.remove(要素)
要素数を取得 vec.size(); len(vec)
配列が空か確認 vec.empty(); len(vec) == 0
全要素を削除 vec.clear(); vec.clear()

array

固定長シーケンスを扱う配列。固定長の配列であるため、宣言時に要素数も指定する必要がある。要素の追加はできないが既存の要素の変更はできる。

Pythonでは(あまり使わないかもしれないが)arrayに近いか。

C++ではこう書く

array<型, 要素数> 変数名;で宣言。この場合、各要素として型に応じたランダムな値が格納される。

array<型, 要素数> 変数名 = {要素1, 要素2, 要素3, ...};と各要素を初期化した上で宣言することも可能。

変数名.at(i)もしくは変数名[i]i番目の要素にアクセスできる。またこのときその要素への代入も可能。vectorと同様、at(i)は境界チェックを行うので前者のほうが安全。

まとめ

C++ Python
宣言 / 定義 array<型, 要素数> arr = {要素1, 要素2, ...}; arr = array.array(型, [要素1, 要素2, ...])
要素を追加 できない arr.append(要素)
先頭要素へのアクセス arr.front(); arr[0]
末尾要素へのアクセス arr.back(); arr[-1]
任意要素へのアクセス(1) 6 arr.at(i); arr[i]
任意要素へのアクセス(2) arr[i]; 7 同上
要素を任意位置に挿入 不可 arr.insert(位置, 要素)
要素を削除 不可 arr.pop() 8
要素を指定して削除 不可 arr.remove(要素)
要素数を取得 arr.size(); len(arr)
配列が空か確認 arr.empty(); len(arr) == 0

list

双方向リンクリストである。片方向リストとしてはforward_listがある。
listは、任意位置における挿入/削除を$O(1)$で実行することができる。しかし、シーケンス内のランダムアクセスは$O(N)$とvectorと逆のようなふるまいをする。

Pythonのlistとは異なり、任意要素へのアクセスはできず、アクセスできるのは先頭/末尾要素のみとなる。

C++ではこう書く

list<型> 変数名;で宣言。

まとめ

C++ Python
宣言 / 定義 list<型> ls; ls = list()
先頭に要素を追加 ls.push_front(); 不可 9
先頭の要素を削除 ls.pop_front(); ls.pop(0) 10
先頭要素へのアクセス ls.front(); ls[0]
末尾に要素を追加 ls.push_back(); ls.append()
末尾の要素を削除 ls.pop_back(); ls.pop()
末尾要素へのアクセス ls.back(); ls[-1]
要素を任意位置に挿入 11 ls.insert(位置, 要素); ls.insert(位置, 要素)
任意要素の削除 ls.erase(位置); 12 ls.remove(要素)
要素数を取得 ls.size(); len(ls)
配列が空か確認 ls.empty(); len(ls) == 0
要素の並び替え 13 ls.sort(); ls.sort()
要素の反転 14 ls.reverse(); ls.reverse()
要素の合成 ls.merge(ls2); ls.extend(ls2)
全要素を削除 ls.clear(); ls.clear()

  1. C++もPythonも境界チェックを行う。すなわち、範囲外にアクセスしたときにC++ではout_of_range例外、PythonではIndexError例外が投げられる 

  2. 境界チェックは行われず、範囲外のインデックスを指定しても動く可能性がある 

  3. C++ではイテレータ / Pythonではインデックスを位置引数として受け取る 

  4. Pythonのpop()は引数を指定しない場合には最後に追加した要素を削除する(vectorpop()と同じ挙動を示す)が、インデックスを引数として渡すことでそのインデックスの要素を削除できる 

  5. 位置引数としてイテレータを受け取り、その位置の要素を削除する 

  6. C++もPythonも境界チェックを行う。すなわち、範囲外にアクセスしたときにC++ではout_of_range例外、PythonではIndexError例外が投げられる 

  7. 境界チェックは行われず、範囲外のインデックスを指定しても動く可能性がある 

  8. 引数を指定しない場合には最後に追加した要素を削除するが、インデックスを引数として渡すことでそのインデックスの要素を削除できる 

  9. ls = [要素] + lsとすれば同様の結果は得られる 

  10. Pythonの場合pop(0)は非常に遅い 

  11. C++ではイテレータ / Pythonではインデックスを位置引数として受け取る 

  12. 位置引数としてイテレータを受け取り、その位置の要素を削除する 

  13. C++もPythonも返り値なし(その場で配列の中身がソートされる) 

  14. C++もPythonも返り値なし(その場で配列の中身が反転される) 

3
1
0

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
3
1