133
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pythonみたいなprint関数をC++で書きたい!

Last updated at Posted at 2019-08-18

#Pythonみたいなprint関数をC++で書きたい!

###はじめに

C++を3週間勉強して、基本的な文法は少しずつ分かってきました。
あとは慣れだと思うので、ひたすらコードを書いて練習しています。

Pythonでは配列の中身をprint関数で表示できて便利ですが、これをC++で再現してみました。

###Pythonのprint関数

Test.py
a = 1
print("Test")
print(a, 3.14, "Hello")
print()
arr = [1, 2, 3, 2]
str_arr = ["Hello", "World"]
print(*arr)
print(*str_arr)
print()
table = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for arr in table:
    print(*arr)

上記を実行すると以下のように出力されます。

Console
Test
1 3.14 Hello

1 2 3 2
Hello World

1 2 3
4 5 6
7 8 9

複数の要素や配列を引数にとり、同様の出力を行う関数をC++で実装します。
配列はvectorを使うことにします。

###C++での実装

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

void print() {
  cout << endl;
}

template <class Head, class... Tail>
void print(Head&& head, Tail&&... tail) {
  cout << head;
  if (sizeof...(tail) != 0) cout << " ";
  print(forward<Tail>(tail)...);
}

template <class T>
void print(vector<T> &vec) {
  for (auto& a : vec) {
    cout << a;
    if (&a != &vec.back()) cout << " ";
  }
  cout << endl;
}

template <class T>
void print(vector<vector<T>> &df) {
  for (auto& vec : df) {
    print(vec);
  }
}

int main() {
  int a = 1;
  print("Test");
  print(a, 3.14, "Hello");
  print();

  vector<int> arr{1, 2, 3, 2};
  vector<string> str_arr{"Hello", "World"};
  print(arr);
  print(str_arr);
  print();

  vector<vector<int>> table{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
  print(table);
}

上記を実行すると以下のように出力されます。

Console
Test
1 3.14 Hello

1 2 3 2
Hello World

1 2 3
4 5 6
7 8 9

###コードの説明

  • 関数のオーバーロード

C++では引数の型が異なる同名の関数を定義することができます。
これを関数のオーバーロードと呼びます。
複数の要素が渡されたときと、配列が渡されたときで、別の処理を書くことができます。

Test.cpp
void print() //引数が空の場合
void print(Head&& head, Tail&&... tail) //引数が1個以上の要素の場合
void print(vector<T> &vec) //引数が配列(vector)の場合
void print(vector<vector<T>> &df) //引数が二重配列の場合
  • 関数テンプレート

引数の要素はint型やdouble型などさまざまですが、要素を表示する処理は同じです。
テンプレートを使うことで、それぞれに対する処理をまとめて書くことができます。

Test.cpp
template <class T> //テンプレートを使うことでvector<int>型やvector<double>型などへの処理を
void print(vector<T> &vec) //この中にまとめて書くことができる

template <class T>
void print(vector<vector<T>> &df) //vector<vector<int>>などの二重配列が引数の場合の処理
  • 可変引数テンプレート

C++11から任意の個数の引数に対応できるテンプレートの書式が追加されました。
この機能を使うことで、複数の要素を渡すことができます。

Test.cpp
template <class Head, class... Tail> //省略記号 ... を書くと可変引数テンプレートになる
void print(Head&& head, Tail&&... tail) {
  cout << head; //最初の要素を出力
  if (sizeof...(tail) != 0) cout << " "; //表示すべき要素が残っていればスペースで区切る
  print(forward<Tail>(tail)...); //最初の要素以外を引数としてprintを呼び出し再帰的に処理
}
  • ユニバーサル参照

再帰的な処理を行うところで、forwardを使ってユニバーサル参照にしています。
参照を使わない場合は以下のようなコードになります。

Test.cpp
template <class Head, class... Tail>
void print(Head head, Tail... tail) {
  cout << head;
  if (sizeof...(tail) != 0) cout << " ";
  print(tail...);
}
  • 範囲for文

C++11ではコンテナの要素を以下の書式で順番に呼び出して処理することができます。
また要素に対して型推論autoを使うことでシンプルに記述ができます。

Test.cpp
  for (auto& a : vec) {
    cout << a; //配列のそれぞれの要素をcoutで順番に出力
    if (&a != &vec.back()) cout << " "; //表示すべき要素が残っていればスペースで区切る
  }

  for (auto& vec : df) {
    print(vec); //二重配列のそれぞれの要素(配列)に対してprint関数を呼び出し
  }

###おわりに

上記のprint関数をコピペして使うと、競技プログラミングで出力したりデバッグするときも便利です。
例えば前回のABC137 B問題では以下のように記述できます。

ABC137
int main() {
  int a, b;
  cin >> a >> b;
  vector<int> arr;
  for (int i = 0; i < 2 * a - 1; ++i) {
    arr.push_back(b - a + 1 + i);
  }
  print(arr);
}

一生懸命勉強して書きましたがまだ修行中の身なので、正確ではない記載があるかもしれません。
コメントやご指摘いただければ幸いです!

###参考文献
C++日本語リファレンス 可変引数テンプレート
C++日本語リファレンス 右辺値参照
C++日本語リファレンス 範囲for文

133
76
11

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
133
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?