LoginSignup
14
6

More than 1 year has passed since last update.

【競技プログラミング用】c++でpython3のprint関数を作ってみた。

Last updated at Posted at 2018-09-29

はじめに

最初に断っておくと、これは競技プログラミングで使うことを想定した仕様です。
なので未定義動作がとてもあります、悪しからず。

追記(2018/10/15)
僕のローカルだと動くんですが他の環境だと動かない部分があったため
chocoboさんからプルリクを頂き少しコードが変わりました。
ありがとうございます。

目的

競技プログラミングをする際に、デバッグ時に便利な標準出力の関数をc++で作ること。

問題文に対する解答として出力を行う際、c++にある出力(cout)とpython3の出力(print)にはそんなに差はないと思います。
ただ競技プログラミングにおけるデバッグとして変数の値を確認するという目的だとcoutというのはprintに比べて少し不便だなと感じます。

cout(c++)

  • 出力の際に変数と変数の間にスペースが自動で入らない。
  • オブジェクトを受けとることができない。
  • タイプ量が多くなる。

print(python3)

  • 出力の際自動で変数と変数の間にスペースが自動でデフォルトで入る。
  • オブジェクトを受けとることができる。
  • 簡潔でタイプ量が少ない。

結局coutでも同じことは出来るんですが、速度を問われる競技プログラミングにおいてはprintの方がやや上回るかなという気がします。
特に多次元リストをそのまま引数に入れることが出来るという点でprintは便利かなと。

ただpythonで競技プログラミングを行うとそれはそれでデメリットもあるので、print関数の仕様を真似てc++で実装していきます。

実装

print.cpp
#include <iostream>
#include <vector>
#include <map>
#include <set>

using namespace std;

template <class T>ostream &operator<<(ostream &o, const vector<T>&obj) {
    o << "{"; for (int i = 0; i < (int)obj.size(); ++i)o << (i > 0 ? ", " : "") << obj[i]; o << "}"; return o;
}

template <class T, class U>ostream &operator<<(ostream &o, const pair<T, U>&obj) {
    o << "{" << obj.first << ", " << obj.second << "}"; return o;
}

template <class T, class U>ostream &operator<<(ostream &o, const map<T, U>&obj) {
    o << "{"; for (auto itr = obj.begin(); itr != obj.end(); ++itr) o << (itr != obj.begin() ? ", " : "") << *itr; o << "}"; return o;
}

template <class T>ostream &operator<<(ostream &o, const set<T>&obj) {
    o << "{"; for (auto itr = obj.begin(); itr != obj.end(); ++itr) o << (itr != obj.begin() ? ", " : "") << *itr; o << "}"; return o;
}

void print(void) {
    cout << endl;
}

template <class Head> void print(Head&& head) {
    cout << head;
    print();
}

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

説明

可変長引数と再帰関数、関数テンプレートとオペレーターのオーバーロードを利用して実装しました。
実装に関して、似たことを先に考えていた人が記事にしていたので参考にしました。
std::vectorの出力 - 赤コーダーになりたい
プリミティブ型、stringやvector、多次元vector、mapやsetを引数に受け取れます。
以下に使用例を載せます。

main.cpp
//上記のコード(print.cpp)は省略
#include <string> 
#include <map>
#include <set>

int main() {

//test1
string s = "hoge";
vector<double> v(5, 7.3);

print(s, v);
/*
hoge {7.3, 7.3, 7.3, 7.3, 7.3}
*/


//test2
vector<vector<int>> vv(2, vector<int>(3, 8));

print(vv);
/*
{{8, 8, 8}, {8, 8, 8}}
*/


//test3 
print();
/*

*/


//test4     
map<string, int> mp;
mp["two"] = 2;
mp["seven"] = 7;
mp["ten"] = 10;
mp["three"] = 3;

set<int> st;
st.insert(1);
st.insert(5);
st.insert(324);
st.insert(34);

vector<map<int, int>> vmp(4);
vmp[0][2] = 9;
vmp[1][5] = 3;
vmp[3][1] = 3;
vmp[1][6] = 2;

print(mp, st, vmp);
/*
{{seven, 7}, {ten, 10}, {three, 3}, {two, 2}} {1, 5, 34, 324} {{{2, 9}}, {{5, 3}, {6, 2}}, {}, {{1, 3}}}
*/

return 0;
}

問題点

queueやstackは受け取れないのが難点。
オペレーターを定義すれば問題ないのでしょうがあまり使わないのでいいかなと。
他にも気づいていないだけで良くない実装がありそう。

おわりに

gistはこちらです。

改良、あるいは上位互換の関数が作れそうな気がするので作った方がいれば教えてください。
また、間違っているところがあれば教えてください。
ご連絡は@ningenMeまで。

追記 (2019/10/11)

とりあえず動くものを使いたい人は下のテンプレを
using namespace std;とmain関数の間に置くと良い感じです。

main.cpp
template <class T, class U>ostream &operator<<(ostream &o, const map<T, U>&obj) {o << "{"; for (auto &x : obj) o << " {" << x.first << " : " << x.second << "}" << ","; o << " }"; return o;}
template <class T>ostream &operator<<(ostream &o, const set<T>&obj) {o << "{"; for (auto itr = obj.begin(); itr != obj.end(); ++itr) o << (itr != obj.begin() ? ", " : "") << *itr; o << "}"; return o;}
template <class T>ostream &operator<<(ostream &o, const multiset<T>&obj) {o << "{"; for (auto itr = obj.begin(); itr != obj.end(); ++itr) o << (itr != obj.begin() ? ", " : "") << *itr; o << "}"; return o;}
template <class T>ostream &operator<<(ostream &o, const vector<T>&obj) {o << "{"; for (int i = 0; i < (int)obj.size(); ++i)o << (i > 0 ? ", " : "") << obj[i]; o << "}"; return o;}
template <class T, class U>ostream &operator<<(ostream &o, const pair<T, U>&obj) {o << "{" << obj.first << ", " << obj.second << "}"; return o;}
template <template <class tmp>  class T, class U> ostream &operator<<(ostream &o, const T<U> &obj) {o << "{"; for (auto itr = obj.begin(); itr != obj.end(); ++itr)o << (itr != obj.begin() ? ", " : "") << *itr; o << "}"; return o;}
void print(void) {cout << endl;}
template <class Head> void print(Head&& head) {cout << head;print();}
template <class Head, class... Tail> void print(Head&& head, Tail&&... tail) {cout << head << " ";print(forward<Tail>(tail)...);}
14
6
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
14
6