C++: Elegantný výpis iterovateľného

Po dlhšom čase opäť prichádzam s malým, ale užitočným kusom kódu, tentokrát v C++.

Tí, čo programujú v C++ iste už použili dvojicu copy a ostream_iterator z STL, kombináciou ktorých jednoducho dokážeme vypísať všetko, čo sa dá prejsť iterátorom.

Tento spôsob ale prináša jednu nevýhodu – oddeľovač sa vypíše aj po poslednom prvku, čo je estetická chyba, ktorá nám môže vadiť. Čítajte ďalej.

Napríklad:

int a[] = {1,2,3,4,5};
vector<int> b(a, a+5);
copy(b.begin(), b.end(), ostream_iterator<int>(cout, ","));
cout << endl;

Vypíše:

1,2,3,4,5,

Riešenie

Pre tých, ktorým formát výstupu nie je ľahostajný, som pripravil jednoduchú funkciu.

template<class T>
void print_range(T begin, T end, 
  string delimiter = ",", ostream& os = cout)
{
T it = begin;
while(it != end)
{
cout << *it;
if(++it != end)
os << delimiter;
}
}

Funkcia nám jednoducho prejde iterátorom (parameter šablóny T) po nejakých dátach, vypíše ich a okrem posledného prvku za každým dá oddeľovač.

Príklad použitia:

int a[] = {1,2,3,4,5};
vector<int> b(a, a+5);
print_range(b.begin(), b.end());
cout << endl;

Vypíše:

1,2,3,4,5

Všimnime si, že kompilátor nám implicitne doplní parameter šablóny, a týmpádom sa nemusíme starať v podstate o nič :).

  • David

    Neslo by to este optimalizovat? Napriklad preco kontrolovat podmienku oddelovaca v kazdej iteracii (riadok c. 06). Nedalo by sa to nahadzat do nejakeho bufferu, a na konci umazat posledny delimiter?

  • Tomáš Srna

    Dajme tomu, ze vypisujeme milion prvkov, z ktorych kazdy ma aspon 100 znakov. To je 95 MB pamate pre buffer. A to este neratam, ze ak by sa nebodaj kopiroval dalsi string – bez posledneho oddelovaca, bolo by to dvakrat tolko. A to len kvoli tomu, ze chceme odmazat posledny delimiter.

    Oproti tomu volanie metody operator!= na objekte iteratora vacsinou len porovnava pointer vo vnutri struktury iteratora, co sa da prezit casovo aj milionkrat, pretoze tato operacia je konstantna.