Fonction séquence-zip pour c ++ 11?

Avec la nouvelle boucle basée sur la plage, nous pouvons écrire du code comme

for(auto x: Y) {} 

Quelle IMO est une énorme amélioration de (par exemple)

 for(std::vector::iterator x=Y.begin(); x!=Y.end(); ++x) {} 

Peut-il être utilisé pour boucler deux boucles simultanées, comme la fonction zip Pythons? Pour ceux qui ne connaissent pas Python, le code:

 Y1 = [1,2,3] Y2 = [4,5,6,7] for x1,x2 in zip(Y1,Y2): print x1,x2 

Donne en sortie (1,4) (2,5) (3,6)

    Attention: boost::zip_iterator et boost::combine as Boost 1.63.0 (26 décembre 2016) entraînera un comportement indéfini si la longueur des conteneurs d’entrée n’est pas la même (il risque de tomber en panne ou de se propager au-delà de la fin).


    A partir de Boost 1.56.0 (7 août 2014), vous pouvez utiliser boost::combine (la fonction existe dans les versions antérieures mais non documentée):

     #include  #include  #include  #include  int main() { std::vector a {4, 5, 6}; double b[] = {7, 8, 9}; std::list c {"a", "b", "c"}; for (auto tup : boost::combine(a, b, c, a)) { // <--- int x, w; double y; std::string z; boost::tie(x, y, z, w) = tup; printf("%d %g %s %d\n", x, y, z.c_str(), w); } } 

    Cela imprimerait

     4 7 a 4
     5 8 b 5
     6 9 c 6
    

    Dans les versions précédentes, vous pouviez définir une plage comme ceci:

     #include  #include  template  auto zip(T&&... containers) -> boost::iterator_range> { auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...)); auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...)); return boost::make_iterator_range(zip_begin, zip_end); } 

    L'utilisation est la même.

    Vous pouvez utiliser une solution basée sur boost::zip_iterator . Créez une classe de conteneur bidon en conservant les références à vos conteneurs et en zip_iterator partir des fonctions de begin et de end . Maintenant vous pouvez écrire

     for (auto p: zip(c1, c2)) { ... } 

    Exemple d’implémentation (veuillez tester):

     #include  #include  template  class zip_container { C1* c1; C2* c2; typedef boost::tuple< decltype(std::begin(*c1)), decltype(std::begin(*c2)) > tuple; public: zip_container(C1& c1, C2& c2) : c1(&c1), c2(&c2) {} typedef boost::zip_iterator iterator; iterator begin() const { return iterator(std::begin(*c1), std::begin(*c2)); } iterator end() const { return iterator(std::end(*c1), std::end(*c2)); } }; template  zip_container zip(C1& c1, C2& c2) { return zip_container(c1, c2); } 

    Je laisse la version variadic comme un excellent exercice pour le lecteur.

    Voir pour une fonction zip qui fonctionne avec range-base for et accepte un nombre quelconque de plages, qui peuvent être des rvalues ​​ou des lvalues ​​et peuvent avoir des longueurs différentes (l’itération s’arrêtera à la fin de la plage la plus courte).

     std::vector vi{ 0, 2, 4 }; std::vector vs{ "1", "3", "5", "7" }; for (auto i : redi::zip(vi, vs)) std::cout << i.get<0>() << ' ' << i.get<1>() << ' '; 

    Prints 0 1 2 3 4 5

    J’ai donc écrit ce zip avant quand je m’ennuyais, j’ai décidé de le poster car il est différent des autres en ce sens qu’il n’utilise pas de boost et ressemble plus au stdlib c ++.

     template  void advance_all (Iterator & iterator) { ++iterator; } template  void advance_all (Iterator & iterator, Iterators& ... iterators) { ++iterator; advance_all(iterators...); } template  Function zip (Function func, Iterator begin, Iterator end, Iterators ... iterators) { for(;begin != end; ++begin, advance_all(iterators...)) func(*begin, *(iterators)... ); //could also make this a tuple return func; } 

    Exemple d’utilisation:

     int main () { std::vector v1{1,2,3}; std::vector v2{3,2,1}; std::vector v3{1.2,2.4,9.0}; std::vector v4{1.2,2.4,9.0}; zip ( [](int i,int j,float k,float l){ std::cout << i << " " << j << " " << k << " " << l << std::endl; }, v1.begin(),v1.end(),v2.begin(),v3.begin(),v4.begin()); } 

    Je suis tombé sur cette même question indépendamment et n’a pas aimé la syntaxe de tout ce qui précède. Donc, j’ai un fichier d’en-tête court qui fait essentiellement la même chose que le fichier zip zipiter mais a quelques macros pour rendre la syntaxe plus acceptable pour moi:

    https://github.com/cshelton/zipfor

    Par exemple, vous pouvez faire

     vector a {1,2,3}; array b {"hello","there","coders"}; zipfor(i,s eachin a,b) cout << i << " => " << s << endl; 

    Le sucre syntaxique principal est que je peux nommer les éléments de chaque conteneur. J'inclus également un "mapfor" qui fait la même chose, mais pour les maps (pour nommer les ".first" et ".second" de l'élément).

     // declare a, b BOOST_FOREACH(boost::tie(a, b), boost::combine(list_of_a, list_of_b)){ // your code here. } 

    Avec range-v3 :

     #include  #include  #include  namespace ranges { template  std::ostream& operator << (std::ostream& os, common_pair const& p) { return os << '(' << p.first << ", " << p.second << ')'; } } using namespace ranges::v3; int main() { std::vector a {4, 5, 6}; double b[] = {7, 8, 9}; std::cout << view::zip(a, b) << std::endl; } 

    Le résultat:

    [(4, 7), (5, 8), (6, 9)]

    Si vous aimez la surcharge des opérateurs, voici trois possibilités. Les deux premiers utilisent respectivement std::pair<> et std::tuple<> comme iterators; le troisième étend cela à la gamme for . Notez que tout le monde n’appréciera pas ces définitions des opérateurs, il est donc préférable de les conserver dans un espace de noms distinct et d’ using namespace un using namespace de using namespace dans les fonctions (pas les fichiers!) Où vous souhaitez les utiliser.

     #include  #include  #include  #include  // put these in namespaces so we don't pollute global namespace pair_iterators { template std::pair operator++(std::pair& it) { ++it.first; ++it.second; return it; } } namespace tuple_iterators { // you might want to make this generic (via param pack) template auto operator++(std::tuple& it) { ++( std::get<0>( it ) ); ++( std::get<1>( it ) ); ++( std::get<2>( it ) ); return it; } template auto operator*(const std::tuple& it) { return std::tie( *( std::get<0>( it ) ), *( std::get<1>( it ) ), *( std::get<2>( it ) ) ); } // needed due to ADL-only lookup template struct tuple_c { std::tuple containers; }; template auto tie_c( const Args&... args ) { tuple_c ret = { std::tie(args...) }; return ret; } template auto begin( const tuple_c& c ) { return std::make_tuple( std::get<0>( c.containers ).begin(), std::get<1>( c.containers ).begin(), std::get<2>( c.containers ).begin() ); } template auto end( const tuple_c& c ) { return std::make_tuple( std::get<0>( c.containers ).end(), std::get<1>( c.containers ).end(), std::get<2>( c.containers ).end() ); } // implement cbegin(), cend() as needed } int main() { using namespace pair_iterators; using namespace tuple_iterators; std::vector ds = { 0.0, 0.1, 0.2 }; std::vector is = { 1, 2, 3 }; std::vector cs = { 'a', 'b', 'c' }; // classical, iterator-style using pairs for( auto its = std::make_pair(ds.begin(), is.begin()), end = std::make_pair(ds.end(), is.end() ); its != end; ++its ) { std::cout << "1. " << *(its.first ) + *(its.second) << " " << std::endl; } // classical, iterator-style using tuples for( auto its = std::make_tuple(ds.begin(), is.begin(), cs.begin()), end = std::make_tuple(ds.end(), is.end(), cs.end() ); its != end; ++its ) { std::cout << "2. " << *(std::get<0>(its)) + *(std::get<1>(its)) << " " << *(std::get<2>(its)) << " " << std::endl; } // range for using tuples for( const auto& d_i_c : tie_c( ds, is, cs ) ) { std::cout << "3. " << std::get<0>(d_i_c) + std::get<1>(d_i_c) << " " << std::get<2>(d_i_c) << " " << std::endl; } } 

    Si vous avez un compilateur compatible C ++ 14 (par exemple, gcc5), vous pouvez utiliser zip fourni par Ryan Haining dans la bibliothèque cppitertools , ce qui semble très prometteur:

     array i{{1,2,3,4}}; vector f{1.2,1.4,12.3,4.5,9.9}; vector s{"i","like","apples","alot","dude"}; array d{{1.2,1.2,1.2,1.2,1.2}}; for (auto&& e : zip(i,f,s,d)) { cout << std::get<0>(e) << ' ' << std::get<1>(e) << ' ' << std::get<2>(e) << ' ' << std::get<3>(e) << '\n'; std::get<1>(e)=2.2f; // modifies the underlying 'f' array } 

    Pour une bibliothèque de traitement de stream C ++, j’écris, je cherchais une solution qui ne repose pas sur des bibliothèques tierces et fonctionne avec un nombre arbitraire de conteneurs. Je me suis retrouvé avec cette solution. Il est similaire à la solution acceptée qui utilise boost (et entraîne également un comportement indéfini si les longueurs de conteneur ne sont pas égales)

     #include  namespace impl { template  class zip_iterator { public: using value_type = std::tuple; zip_iterator(const Iter &head, const Iters&... tail) : head_(head), tail_(tail...) { } value_type operator*() const { return std::tuple_cat(std::tuple(*head_), *tail_); } zip_iterator& operator++() { ++head_; ++tail_; return *this; } bool operator==(const zip_iterator &rhs) const { return head_ == rhs.head_ && tail_ == rhs.tail_; } bool operator!=(const zip_iterator &rhs) const { return !(*this == rhs); } private: Iter head_; zip_iterator tail_; }; template  class zip_iterator { public: using value_type = std::tuple; zip_iterator(const Iter &head) : head_(head) { } value_type operator*() const { return value_type(*head_); } zip_iterator& operator++() { ++head_; return *this; } bool operator==(const zip_iterator &rhs) const { return head_ == rhs.head_; } bool operator!=(const zip_iterator &rhs) const { return !(*this == rhs); } private: Iter head_; }; } // namespace impl template  class seq { public: using iterator = Iter; seq(const Iter &begin, const Iter &end) : begin_(begin), end_(end) { } iterator begin() const { return begin_; } iterator end() const { return end_; } private: Iter begin_, end_; }; /* WARNING: Undefined behavior if iterator lengths are different. */ template  seq> zip(const Seqs&... seqs) { return seq>( impl::zip_iterator(std::begin(seqs)...), impl::zip_iterator(std::end(seqs)...)); } 

    Boost.Iterators a zip_iterator vous pouvez utiliser (exemple dans les docs). Cela ne marchera pas avec range, mais vous pouvez utiliser std::for_each et un lambda.

    std :: transform ne peut pas le faire sortingvialement:

     std::vector a = {1,2,3,4,5}; std::vector b = {1,2,3,4,5}; std::vectorc; std::transform(a.begin(),a.end(), b.begin(), std::back_inserter(c), [](const auto& aa, const auto& bb) { return aa*bb; }); for(auto cc:c) std::cout< 

    Si la seconde séquence est plus courte, mon implémentation semble donner des valeurs initialisées par défaut.

    Voici une version simple qui ne nécessite pas de boost. Il ne sera pas particulièrement efficace car il crée des valeurs temporaires et ne généralisera pas sur des conteneurs autres que des listes, mais il ne comporte aucune dépendance et résout le cas le plus courant de compression.

     template std::list< std::pair > zip(std::list left, std::list right) { auto l = left.begin(); auto r = right.begin(); std::list< std::pair > result; while( l!=left.end() && r!=right.end() ) result.push_back( std::pair( *(l++), *(r++) ) ); return result; } 

    Bien que les autres versions soient plus flexibles, il est souvent utile d’utiliser un opérateur simple pour créer une liste unique. Cette version a l’avantage que le cas commun est simple.