【C++】repマクロとは?使わないほうがいい?
競技プログラミングをやってると、rep マクロを使っているコードによく出会います。1 文字でも入力を短くして入力をはやくして、バグの原因を減らすのが目的です。
マクロは書き方が自由なので、人によって書き方に違いが出てきてしまいます。私の感覚だと、あまり使いたくない機能です。
マクロとは
ソースの文字を別の文字に置き換えるようにできるものです。
#define 置き換える前の文字列 置き換える後の文字列で定義します。
#include <bits/stdc++.h>
using namespace std;
#define HELLO "hello, world!"
int main() {
cout << HELLO;
}
と書くとコンパイル時にhelloが“hello, world!”に置き換えられます。上のソースと下のソースは同義です。
#include <bits/stdc++.h>
using namespace std;
int main() {
cout << "hello, world!";
}
関数形式マクロ
単純な文字列の置き換えだけでなく、メソッドのような感じで引数みたいなものも使えます。 置き換える前の文字列の後ろにカッコを付けると、引数にみたいな形でマクロに渡せます。
#include <bits/stdc++.h>
using namespace std;
#define IF_HELLO(V) if((V) == "hello")
int main() {
IF_HELLO("hello") {
cout << "true";
} else {
cout << "false";
}
}
とすると、下のソースに置き換えられます。
#include <bits/stdc++.h>
using namespace std;
int main() {
if(("hello") == "hello) {
cout << "true";
} else {
cout << "false";
}
}
文字列を置き換えるだけ
関数の定義のように見えるけど、単純に文字列を置き換えているだけです。 なので、こんな不気味な書き方もできちゃいます。
#include <bits/stdc++.h>
using namespace std;
// カッコ始まりだけ書いてみる
#define IF_HELLO(V) if((V) == "hello") {
int main() {
// マクロで始まりが書いてあるから、始まりは書かない
IF_HELLO("hello")
cout << "true";
} // 閉じカッコは書く
}
rep マクロとは
APG4bにAP4 - 付録 4.ループの裏技 rep マクロという記事があるので、こちらを参照してください。
便利は便利です。
表記がブレる
APG4b で紹介されている rep マクロは ↓ です。
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
私が普段使用している rep マクロは ↓ です。
#define REP(i, n) for (int i = 0; i < (int)(n); i++)
マクロは UPPER CASE にする場合が多いですが、競プロの rep マクロは小文字にしている方が多いです。なるべく普通の書き方をしたいので、私は大文字にしています。同じ機能で同じ書き方をしたいのに、表記がブレてしまうのがいただけないです・・
C 言語の書き方であること
C++はオブジェクト指向プログラミング言語です。rep マクロは C 言語に寄った書き方なので、C++っぽい書き方をしたほうが好ましいです。
例えば rep マクロを書きたい頻出シーンは、リストすべてを全探索したい場合です。そういう場合は(範囲 for 文)[https://atcoder.jp/contests/apg4b/tasks/APG4b_r]を使いましょう。
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<string> v = {"hoge", "fuga", "piyo", "foo", "bar"};
for(auto&& str : v) {
cout << str << endl;
}
}
こういう問題が競技プログラミングで出題される場合、n に入力する数が設定され、その後に文字列が渡されるような入力になる場合が多いです。
5
hoge
fuga
piyo
foo
bar
その場合は先に vector を作っておいて、範囲 for 文で書くとわかりやすいです。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<string> v(n);
for(auto&& str : v) {
cin >> str;
}
for(auto&& str : v) {
cout << str << endl;
}
}
この 2 パターンでまかなえないループは、初期化も条件も更新も複雑になものです。つまり rep マクロでは実現できません。
一周回って for が便利
範囲 for 文も使えるし、複雑な条件も書ける for 文が一周回って便利です。そして rep マクロにたより続けると C++らしい書き方の練習になりません。ぜひ拡張 for や、今回紹介しませんでしたが(イテレータ)[https://atcoder.jp/contests/apg4b/tasks/APG4b_ai]を使った for 文もあるので、ぜひ色んな書き方に親しんでみましょう!