Z - 3.02.pair/tupleとauto /

Time Limit: 0 msec / Memory Limit: 0 KB

前のページ | 次のページ

キーポイント

pair

  • pair型は2つの値の組を表す
  • pair<値1の型, 値2の型> 変数名;で宣言する
  • 変数名.firstで1番目の値、変数名.secondで2番目の値にアクセスできる
  • make_pair(値1, 値2)でpairを生成することができる
  • tie(変数1, 変数2) = pair型の値;でpairを分解することができる

tuple

  • tuple型は複数個の値の組を表す
  • tuple<値1の型, 値2の型, 値3の型, (...)> 変数名;(必要な分だけ型を書く)で宣言する
  • make_tuple(値1, 値2, 値3, (...))でtupleを生成することができる
  • tie(変数1, 変数2, 変数3, (...)) = tuple型の値;でtupleを分解することができる

pair/tupleの比較

  • 1番目の値から比較され、等しい場合は次の値で比較される

auto

  • 変数宣言や範囲for文において、autoを用いることで、型を省略して書くことができる
  • autoで参照を作ることもできる

pair/tuple

pair

pairは2つの値の組を表す型です。

サンプルコード

#include <bits/stdc++.h>
using namespace std;

int main() {
  pair<string, int> p("abc", 3);
  cout << p.first << endl;  // abc

  p.first = "hello";
  cout << p.first << endl;  // hello
  cout << p.second << endl; // 3

  p = make_pair("*", 1);

  string s;
  int a;
  tie(s, a) = p;
  cout << s << endl;  // *
  cout << a << endl;  // 1
}
実行結果
abc
hello
3
*
1

宣言・初期化

pair<型1, 型2> 変数名;
pair<型1, 型2> 変数名(値1, 値2);

アクセス

変数名.first   // 1つ目の値
変数名.second  // 2つ目の値

pairの生成

make_pair(値1, 値2)

make_pairによってpair型の値を得ることができます。

また、次のようにすることでもpairを生成することができます。

pair<型1, 型2>(値1, 値2)

どちらを使っても構いませんが、make_pairでは型を明示的に書く必要がないのでmake_pairを使うとよいでしょう。

細かい話で扱う型エイリアスを用いてpairの型に簡単な名前を付ける場合、後者の方が楽に書ける場合もあります。

pairの分解

型1 変数1;
型2 変数2;
tie(変数1, 変数2) = pair型の値;

変数1、変数2にそれぞれpairの1番目の値、2番目の値が代入されます。

tuple

tupleはpairを一般化したもので、「複数個の値の組」を表す型です。

サンプルコード

#include <bits/stdc++.h>
using namespace std;

int main() {
  tuple<int, string, bool> data(1, "hello", true);
  get<2>(data) = false;
  cout << get<1>(data) << endl;  // hello

  data = make_tuple(2, "WORLD", true);

  int a;
  string s;
  bool f;
  tie(a, s, f) = data;
  cout << a << " " << s << " " << f << endl;  // 2 WORLD 1
}
実行結果
hello
2 WORLD 1

宣言・初期化

tuple<型1, 型2, 型3, ...(必要な分だけ型を書く)> 変数名;
tuple<型1, 型2, 型3, ...(必要な分だけ型を書く)> 変数名(値1, 値2, 値3, ...); // 初期化

tupleでは0個以上の組を表現することができます。

アクセス

get<K>(tuple型の変数)  // K(定数)番目にアクセス

Kは定数でなければならないことと、Kは0から始まる数であることに注意してください。

次のように、Kとして変数を用いることはできません。

int i = 2;
tuple<int, int, int> data(1, 2, 3);
cout << get<i>(data) << endl;  // コンパイルエラー

tupleの生成

make_tuple(値1, 値2, 値3, ...)

以下のようにして生成することもできます。

tuple<型1, 型2, 型3, ...(必要な分だけ書く)>(値1, 値2, 値3, ...)

tupleの分解

型1 変数1;
型2 変数2;
型3 変数3;
︙
tie(変数1, 変数2, 変数3, ...) = tuple型の値;

pair/tupleの比較

型が同じpairやtuple同士は比較することができます。

例えばpair<int, int>を比較する場合、1番目の値を基準に比較され、もし1番目の値が等しい場合は2番目の値を基準に比較されます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  pair<int, int> a(3, 1);
  pair<int, int> b(2, 10);
  // 3 > 2 なので a < b は false
  if (a < b) {
    cout << "a < b" << endl;
  }
  else {
    cout << "a >= b" << endl;
  }

  pair<int, int> c(5, 1);
  pair<int, int> d(5, 10);
  // 5 == 5 であり 1 < 10 なので c < d は true
  if (c < d) {
    cout << "c < d" << endl;
  }
  else {
    cout << "c >= d" << endl;
  }
}
実行結果
a >= b
c < d

tupleを比較する場合もpairと同様に「1番目の値が最優先で比較され、等しい場合は2番目の値で比較する。もし2番目の値も等しい場合は3番目の値を比較する……」と、1番目の値から順に比較されます。

なお、==は全ての値が等しい場合、!=は1つ以上の異なる値が存在する場合にtrueとなります。

pairやtupleの配列をソートする場合も上記の比較順序でソートされます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<tuple<int, int, int>> a;
  a.push_back(make_tuple(3, 1, 1));
  a.push_back(make_tuple(1, 2, 100));
  a.push_back(make_tuple(3, 5, 1));
  a.push_back(make_tuple(1, 2, 3));
  sort(a.begin(), a.end());

  for (tuple<int, int, int> t : a) {
    int x, y, z;
    tie(x, y, z) = t;
    cout << x << " " << y << " " << z << endl;
  }
}
実行結果
1 2 3
1 2 100
3 1 1
3 5 1

auto

初期化を伴って変数を宣言する場合や範囲for文において、型の部分にautoと書くことによって型を省略することができます。

サンプルコード

#include <bits/stdc++.h>
using namespace std;

string concat(string a, string b) {
  return a + b;
}

int main() {
  string a = "Hello,";
  string b = "World";
  auto ab = concat(a, b);  // string型
  cout << ab << endl;

  vector<int> c = {1, 2, 3};
  auto d = c;  // vector<int>型

  for (auto elem : d) {
    // elemはint型
    cout << elem << endl;
  }
}
実行結果
Hello,World
1
2
3

変数宣言でのauto

auto 変数名 = 初期値;

範囲for文でのauto

for (auto 変数名 : 配列変数) {
  // 変数名 を使う
}

参照でのauto

int a = 12345;
auto &ref = a;  // aへの参照 `int &ref = a;`と書くのと同じ

vector<int> b = {1, 2, 3};
// 範囲for文でも参照にすることができる
for (auto &elem : b) {
  elem *= 2;
}
cout << b.at(0) << endl;  // 2
cout << b.at(1) << endl;  // 4
cout << b.at(2) << endl;  // 6

細かい話

ignore

pairやtupleを分解する際に要らない要素を捨てたい場合、ignoreをtieの引数に渡すことで、対応する位置の値を捨てることができます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  pair<int, int> p(3, 5);
  int right;
  tie(ignore, right) = p;  // 2番目の値だけ取り出す
  cout << right << endl; // 5

  tuple<int, string, bool> tpl(2, "hello", false);
  int x;
  string s;
  tie(x, s, ignore) = tpl;  // 1番目の値、2番目の値だけ取り出す
  cout << x << " " << s << endl; // 2 hello
}
実行結果
5
2 hello

型エイリアス

型エイリアスを用いると型に別の名前をつけることができます。 pairやtupleなど型名が長くなってしまう場合に型エイリアスを使うと便利です。

#include <bits/stdc++.h>
using namespace std;

int main() {
  using pii = pair<int, int>;
  // これ以降 pii という型名はpair<int, int> と同じ意味で使える

  pii p;
  p = make_pair(1, 2);
  cout << "(" << p.first << ", " << p.second << ")" << endl;
  p = pii(3, 4);  // 別のpairを作って代入
  cout << "(" << p.first << ", " << p.second << ")" << endl;
}
実行結果
(1, 2)
(3, 4)

次のようにして型エイリアスを宣言することができます。

型エイリアスの宣言

using 新しい型名 = 型名;

宣言した型エイリアスは型名として利用できます。

型エイリアスを用いることで、多次元配列の型を簡単に書くこともできます。

using vi = vector<int>; // intの1次元の型に vi という別名をつける
using vvi = vector<vi>; // intの2次元の型に vvi という別名をつける

int N = 10, M = 20;
vvi data(N, vi(M));  // N * M の2次元配列

これまでプログラムの2行目に書いていたusing namespace std;にもusingというキーワードが出てきますが、このusingと型エイリアスのusingは違う意味なので注意してください。using namespace std;については4章で説明します。

なお、C++では同様の機能としてtypedef宣言が用意されていますが、基本的にはusingを使うようにしましょう。

#include <bits/stdc++.h>
using namespace std;

int main() {
  typedef pair<int, int> pii;  // pair<int, int> に pii という別名を付ける

  pii p = make_pair(1, 2);
  cout << "(" << p.first << ", " << p.second << ")" << endl;
}
実行結果
(1, 2)

演習問題

リンク先の問題を解いてください。

ABCの問題

今回紹介した機能を用いて解くことができるAtCoder Beginner Contestの問題を紹介します。 練習問題だけでは物足りない人は是非挑戦してみてください。

前のページ | 次のページ