2014-12-20 6 views
17

Ad esempio:Come posso controllare se sono l'ultimo elemento quando l'iterazione utilizzando la sintassi foreach

for(auto &iter: item_vector) { 
    if(not_on_the_last_element) printf(", "); 
} 

o

for(auto &iter: skill_level_map) { 
    if(not_on_the_last_element) printf(", "); 
} 
+0

Solo una nota: invece di controllare se sei sull'ultimo elemento, puoi spostare la stampa di "," all'inizio del corpo del ciclo e controllare se sei sul primo elemento. È facile da fare impostando 'bool first = true;' prima del ciclo e impostando 'first = false;' all'interno del corpo del loop. – hvd

+1

Perché non utilizzare il ['ostream_joiner'] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4336.html#iterator.ostream.joiner) dai Fondamenti? –

risposta

21

Non si può davvero. Questo è un po 'il punto del raggio d'azione, perché è che non hai bisogno di iteratori. Ma si può semplicemente cambiare la logica di come si stampa la virgola per stamparla se non è prima:

bool first = true; 
for (auto& elem : item_vector) { 
    if (!first) printf(", "); 
    // print elem 
    first = false; 
} 

Se questo è l'intento del ciclo in ogni caso. Oppure si potrebbe confrontare gli indirizzi:

for (auto& elem : item_vector) { 
    if (&elem != &item_vector.back()) printf(", "); 
    // ... 
} 
+0

cool trick (primo frammento), ** + 1 **! – vsoftco

6

Non c'è grande metodo. Ma se abbiamo un facile accesso all'ultimo elemento del contenitore ...

std::vector<int> item_vector = ...; 
for (auto & elem : item_vector) { 
    ... 
    if (&elem != &item_vector.back()) 
     printf(", "); 
} 
1

negozio di questo codice via in modo sicuro in un file di intestazione nel tuo piccolo sacchetto di utility:

namespace detail { 
    template<class Iter> 
    struct sequence_emitter 
    { 
     sequence_emitter(Iter first, Iter last, std::string sep) 
     : _first(std::move(first)) 
     , _last(std::move(last)) 
     , _sep(std::move(sep)) 
     {} 

     void write(std::ostream& os) const { 
      bool first_element = true; 
      for (auto current = _first ; current != _last ; ++current, first_element = false) 
      { 
       if (!first_element) 
        os << _sep; 
       os << *current; 
      } 
     } 

    private: 
     Iter _first, _last; 
     std::string _sep; 
    }; 

    template<class Iter> 
    std::ostream& operator<<(std::ostream& os, const sequence_emitter<Iter>& se) { 
     se.write(os); 
     return os; 
    } 
} 

template<class Iter> 
detail::sequence_emitter<Iter> 
emit_sequence(Iter first, Iter last, std::string separator = ", ") 
{ 
    return detail::sequence_emitter<Iter>(std::move(first), std::move(last), std::move(separator)); 
} 

allora si può emettere qualsiasi intervallo di qualsiasi contenitore senza un separatore finale in questo modo:

vector<int> x { 0, 1, 2, 3, 4, 5 }; 
cout << emit_sequence(begin(x), end(x)) << endl; 

set<string> s { "foo", "bar", "baz" }; 

cout << emit_sequence(begin(s), end(s), " comes before ") << endl; 

risultato atteso:

0, 1, 2, 3, 4, 5 
bar comes before baz comes before foo 
0

I cicli basati su loop sono fatti per iterare su tutta la gamma. Se non lo vuoi, perché non fai un ciclo regolare?

auto end = vector.end() - 1; 
for (auto iter = vector.begin(); iter != end; ++iter) { 
    // do your thing 
    printf(", "); 
} 
// do your thing for the last element 

Se non si vuole ripetere il codice due volte per "fare le tue cose", come avrei, quindi creare un lambda che fa una chiamata è:

auto end = vector.end() - 1; 
// create lambda 
for (auto iter = vector.begin(); iter != end; ++iter) { 
    lambda(*iter); 
    printf(", "); 
} 
lambda(vector.back()); 
4

Questi tipo di loop sono i migliori scritti utilizzando il "Loop and a Half" costrutto:

#include <iostream> 
#include <vector> 

int main() 
{ 
    auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6}; 

    auto first = begin(somelist), last = end(somelist); 
    if (first != last) {    // initial check 
     while (true) { 
      std::cout << *first++;  
      if (first == last) break; // check in the middle 
      std::cout << ", "; 
     } 
    } 
} 

Live Example che stampa

1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 6

cioè senza separatore alla fine dell'ultimo elemento.

Il controllo nel mezzo è ciò che lo rende diverso da do-while (check up front) o for_each/range-based per (controllare alla fine). Cercando di forzare un ciclo regolare per questi loop introdurrà rami extra condizionali o logica di programma duplicata.

1

Questo è come un modello di stato.

#include <iostream> 
#include <vector> 
#include <functional> 

int main() { 
    std::vector<int> example = {1,2,3,4,5}; 

    typedef std::function<void(void)> Call; 
    Call f = [](){}; 
    Call printComma = [](){ std::cout << ", "; }; 
    Call noPrint = [&](){ f=printComma; }; 
    f = noPrint; 

    for(const auto& e:example){ 
     f(); 
     std::cout << e; 
    } 

    return 0; 
} 


Output: 

1, 2, 3, 4, 5 

La prima volta attraverso f punti noPrint che serve solo a rendere f quindi selezionare printComma, così virgole vengono stampate solo prima della seconda e successivi articoli.

Problemi correlati