#include <vector>
#include <iostream>
#include <sstream>
#include <boost/foreach.hpp>
using namespace std;
int main()
{
vector<int> VecInts;
VecInts.Push_back(1);
VecInts.Push_back(2);
VecInts.Push_back(3);
VecInts.Push_back(4);
VecInts.Push_back(5);
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
ss << i << ",";
}
cout << ss.str();
return 0;
}
これは出力します:1,2,3,4,5,
ただし、次のものが必要です:1,2,3,4,5
elegantの方法でそれをどのように達成できますか?
「エレガント」とは何を意味するのかについて、いくつか混乱があるようです。ループで「if句」の速度が低下することはありません。ベクターの100.000エントリを想像してみてください。それがあなたが提供しなければならないすべてであるならば、私はループを通過した後にむしろ最後のカンマを削除したいです。
これはどう:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>
int main()
{
std::vector<int> v;
v.Push_back(1);
v.Push_back(2);
v.Push_back(3);
v.Push_back(4);
v.Push_back(5);
std::ostringstream ss;
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<int>(ss, ", "));
ss << v.back();
std::cout << ss.str() << "\n";
}
変数を追加する必要はなく、ブーストに依存することさえありません!実際には、「ループに追加の変数がない」という要件に加えて、ループすら存在しないと言えます:)
最後の前のものを検出することは常にトリッキーであり、最初のものを検出することは非常に簡単です。
bool first = true;
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
if (!first) { ss << ","; }
first = false;
ss << i;
}
Boost SpiritからKarmaを使用-高速であるとの評判があります。
#include <iostream>
#include <vector>
#include <boost/spirit/include/karma.hpp>
int main()
{
std::vector<int> v;
v.Push_back(1);
v.Push_back(2);
v.Push_back(3);
using namespace boost::spirit::karma;
std::cout << format(int_ % ',', v) << std::endl;
}
試してください:
if (ss.tellp ())
{
ss << ",";
}
ss << i;
あるいは、「もし」があなたを心配させているなら:
char *comma = "";
BOOST_FOREACH(int i, VecInts)
{
ss << comma << i;
comma = ",";
}
個人的には、潜在的なメモリ割り当てを引き起こさないソリューションが好きです(文字列が必要以上に大きくなるため)。ループ本体内の追加のifは、ブランチターゲットバッファリングのおかげで扱いやすいはずですが、私はそうします:
#include <vector>
#include <iostream>
int main () {
using std::cout;
typedef std::vector<int>::iterator iterator;
std::vector<int> ints;
ints.Push_back(5);
ints.Push_back(1);
ints.Push_back(4);
ints.Push_back(2);
ints.Push_back(3);
if (!ints.empty()) {
iterator it = ints.begin();
const iterator end = ints.end();
cout << *it;
for (++it; it!=end; ++it) {
cout << ", " << *it;
}
cout << std::endl;
}
}
または、BYORA(独自の再利用可能なアルゴリズムを導入):
// Follow the signature of std::getline. Allows us to stay completely
// type agnostic.
template <typename Stream, typename Iter, typename Infix>
inline Stream& infix (Stream &os, Iter from, Iter to, Infix infix_) {
if (from == to) return os;
os << *from;
for (++from; from!=to; ++from) {
os << infix_ << *from;
}
return os;
}
template <typename Stream, typename Iter>
inline Stream& comma_seperated (Stream &os, Iter from, Iter to) {
return infix (os, from, to, ", ");
}
そのため
...
comma_seperated(cout, ints.begin(), ints.end()) << std::endl;
infix(cout, ints.begin(), ints.end(), "-") << std::endl;
infix(cout, ints.begin(), ints.end(), "> <") << std::endl;
...
出力:
5, 1, 4, 2, 3
5-1-4-2-3
5> <1> <4> <2> <3
すてきなことは、すべての出力ストリーム、前方反復子を持つ任意のコンテナー、任意の接頭辞、および任意の接頭辞タイプ(たとえば、ワイド文字列を使用する場合に興味深い)で機能します。
テストをループの外に移動するのが好きです。
それは一度だけ実行する必要があります。最初にそれを行います。
このような:
if (!VecInts.empty())
{
ss << VecInts[0]
for(any loop = ++(VecInts.begin()); loop != VecInts.end(); ++loop)
{
ss << "," << *loop;
}
}
最後に文字列をトリムするか、foreachの代わりに単一のforループを使用して、最後の反復で連結しないでください。
まあ、とにかくstringstream
にフォーマットする場合は、結果の文字列を1文字だけトリミングできます。
cout << ss.str().substr(0, ss.str().size() - 1);
文字列が空の場合、2番目の引数は-1と表示されます。これはすべてを意味し、クラッシュしません。文字列が空でない場合は、常にカンマで終わります。
しかし、出力ストリームに直接書き込む場合、first
フラグよりも優れたものは見つかりませんでした。
boost.string algo からjoin
を使用する場合を除きます。
これはうまくいくでしょう
stringstream ss;
BOOST_FOREACH(int const& i, VecInts)
{
if(&i != &VecInts[0])
ss << ", ";
ss << i;
}
「エレガント」とは、「新しい変数を導入せずに」という意味だと思います。しかし、他に何も見つけることができなかった場合、私はそれを「エレガントでない」だけでやると思います。まだはっきりしています
stringstream ss;
bool comma = false;
BOOST_FOREACH(int i, VecInts)
{
if(comma)
ss << ", ";
ss << i;
comma = true;
}
ベクターの100.000エントリを想像してみてください。それがあなたが提供しなければならないすべてであるならば、私はループを通過した後、私はむしろ最後のカンマを削除したいと思います。
あなたはそれを印刷するかのように言っていますss << i
は1つの機械語命令です。さあ、その式を実行すると、たくさんのif
とループが実行されます。あなたのif
はそれと比較して何もありません。