別名ケツカンマ問題。
最後の要素の後にはカンマを入れてはいけない、という場合にどう対処するか。
要素のインデックスやイテレータを使ってループしているなら、普通にそれを見ればいい。
std::cout << "["; for(std::size_t i=0; i<v.size(); ++i) { std::cout << to_string(v.at(i)); if(i + 1 != v.size()) { std::cout << ", "; } } std::cout << "]";
とはいえ、インデックスはtypoしやすいし、インデックスの値そのものが必要でないならrange based for loopを使いたい。 だが、range based for loopでは今が先頭かどうか判定する方法がないので、フラグを外に用意しないといけない。
std::cout << "["; { bool is_front = true; for(const auto& elem : v) { if(not is_front) { std::cout << ", "; } std::cout << to_string(elem); is_front = false; } } std::cout << "]";
is_front
のようなフラグが漏れ出してしまうのを防ぐには、スコープを導入しなければならない。とはいえこれは不恰好だ。
C++20ではforで初期化式が使えるようになるので、この外のスコープが不要になり少しだけ簡潔になる。
std::cout << "["; for(bool is_front = true; const auto& elem : v) { if(not is_front) { std::cout << ", "; } std::cout << to_string(elem); is_front = false; } std::cout << "]";
だが、直接cout
に書き出すのでなく文字列を用意して良いのなら、そして最初と最後に括弧が入るなら、C++11の範囲内ですらループ内から分岐を追い払うことが可能になる。最後のコンマと最初のスペースを、ループが終わったあとで括弧に置き換えれば良い。そのために、値のあとで", "
を書き込んでいたのを、,
とに分解して前後に持ってくる。
std::string str; for(const auto& elem : v) { str += ' '; str += to_string(elem); str += ','; } if(v.empty()){str.resize(2);} str.front() = '['; str.back() = ']'; std::cout << str;
front
とback
は境界チェックをしないので、空だった時は別に領域を確保しておかないといけないことに気をつけよう。
埋めるべき括弧がフォーマット上存在しない時も、pop_back
を使って最後のコンマをあとで消すこともできる。pop_back
なら定数時間だ。
ループ内から分岐をなくせたのはよかったが、コードを見てみると思ったほどシンプルにはならなかったな。