web-dev-qa-db-fra.com

Comment puis-je vérifier si je suis sur le dernier élément lors d'une itération à l'aide de la syntaxe foreach

Par exemple:

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

ou

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

Tu ne peux pas vraiment. C'est un peu le point de départ, c'est que vous n'avez pas besoin d'itérateurs. Mais vous pouvez simplement changer de logique pour imprimer la virgule si ce n’est pas la première:

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

Si tel est le but de la boucle quand même. Ou vous pouvez comparer les adresses:

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

Il n'y a pas de bonne méthode. Mais si nous avons un accès facile au dernier élément du conteneur ...

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

Il est préférable d’écrire ces types de boucles en utilisant la construction "Boucle et demi":

#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 << ", ";
        }
    }
}

Exemple en direct qui imprime 

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

c'est-à-dire sans séparateur à la fin du dernier élément.

Le contrôle au milieu est ce qui le rend différent de do-while (contrôle en amont) ou de for_each/range-based for (contrôle à la fin). Essayer de forcer une boucle for régulière sur ces boucles introduira soit des branches conditionnelles supplémentaires, soit une logique de programme en double.

4
TemplateRex

stockez ce code en toute sécurité dans un fichier d’en-tête de votre petit paquet d’utilitaires:

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));
}

alors vous pouvez émettre n'importe quelle plage de n'importe quel conteneur sans séparateur final comme ceci:

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;

production attendue:

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

C'est comme un modèle d'état.

#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 première fois à travers f pointe sur noPrint qui sert uniquement à rendre f puis pointe sur printComma, de sorte que les virgules ne sont imprimées qu'avant le deuxième élément et les suivants.

2
quamrana

Les boucles basées sur la plage sont faites pour parcourir la plage entière. Si vous ne le souhaitez pas, pourquoi ne faites-vous pas une boucle régulière?

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

Si vous ne voulez pas répéter le code deux fois pour "faire ce que vous voulez", créez un lambda qui l'appelle:

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