Ce que je veux faire : Je veux trier 2, ou 3, ou N vecteurs, verrouillés ensemble, sans les copier en un tuple. C'est-à-dire, en laissant de côté la verbosité, quelque chose comme :
vector<int> v1 = { 1, 2, 3, 4, 5};
vector<double> v2 = { 11, 22, 33, 44, 55};
vector<long> v3 = {111, 222, 333, 444, 555};
typedef tuple<int&,double&,long&> tup_t;
sort(zip(v1,v2,v3),[](tup_t t1, tup_t t2){ return t1.get<0>() > t2.get<0>(); });
for(auto& t : zip(v1,v2,v3))
cout << t.get<0>() << " " << t.get<1>() << " " << t.get<2>() << endl;
Cela devrait donner un résultat :
5 55 555
4 44 444
...
1 11 111
Comment je le fais en ce moment : J'ai implémenté mon propre quicksort, où le premier tableau que je passe est utilisé pour la comparaison, et les permutations sont appliquées à tous les autres tableaux. Je n'ai pas réussi à trouver comment réutiliser std::sort pour mon problème (par exemple, extraire les permutations).
Ce que j'ai essayé : boost::zip_iterator y boost::zip_range (avec boost::combine range), mais std::sort et boost::range::algorithme::sort se plaindre que les itérateurs/plages sont en lecture seule et non en accès aléatoire...
Question : Comment trier N vecteurs en lock step (zippé) ? Le problème semble assez générique et commun, donc je suppose qu'il doit y avoir une solution facile à travers une bibliothèque probablement très complexe, mais je ne la trouve pas...
Remarques : oui, il y a des questions similaires sur stackoverflow, cette question est souvent posée sous différentes formes. Cependant, elles se terminent toujours par l'une des réponses suivantes :
- Copiez vos vecteurs dans une paire/tuple et triez ce tuple...
- Copiez vos vecteurs dans un struct avec un membre par vecteur et triez un vecteur de structs...
- Implémentez votre propre fonction de tri pour votre problème particulier...
- Utiliser un tableau auxiliaire d'indices...
- Utilisez boost::zip_iterator sans exemple ou avec un exemple qui donne de mauvais résultats.
Des conseils :
- J'ai trouvé ce fil de discussion dans le liste d'adresses de relance qui pointe vers ce papier d'Anthony Williams. Bien que cela semble ne fonctionner que pour les paires, ils discutent également un TupleIteratorType mais je n'ai pas été capable de le trouver.
- utilisateur673679 trouvé este post contenant une belle solution pour le cas des deux conteneurs. Il définit également le problème (c'est moi qui souligne) :
[...] le problème fondamental est que les "paires" de références de tableaux ne se comportent pas comme elles le devraient [...] J'ai simplement décidé d'abuser de la notation d'un itérateur et d'écrire quelque chose qui fonctionne. Cela a impliqué l'écriture, en fait, d'un itérateur non-conforme où la référence du type de valeur n'est pas la même que celle du type de référence.
Réponse : voir le commentaire d'interjay ci-dessous (ceci répond aussi partiellement à la question future question ):
#include "tupleit.hh"
#include <vector>
#include <iostream>
#include <boost/range.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <boost/range/algorithm/for_each.hpp>
template <typename... T>
auto zip(T&... containers)
-> boost::iterator_range<decltype(iterators::makeTupleIterator(std::begin(containers)...))> {
return boost::make_iterator_range(iterators::makeTupleIterator(std::begin(containers)...),
iterators::makeTupleIterator(std::end(containers)...));
}
int main() {
typedef boost::tuple<int&,double&,long&> tup_t;
std::vector<int> a = { 1, 2, 3, 4 };
std::vector<double> b = { 11, 22, 33, 44 };
std::vector<long> c = { 111, 222, 333, 444 };
auto print = [](tup_t t){ std::cout << t.get<0>() << " " << t.get<1>() << " " << t.get<2>() << std::endl; };
boost::for_each( zip(a, b, c), print);
boost::sort( zip(a, b, c), [](tup_t i, tup_t j){ return i.get<0>() > j.get<0>(); });
for ( auto tup : zip(a, b, c) ) print(tup);
return 0;
}
Question à venir : la réponse précédente fonctionne pour les conteneurs de séquence. Pourrions-nous la faire fonctionner également sur triable des conteneurs (par exemple, des séquences et des listes) ? Cela nécessiterait un accès aléatoire et des TupleIterators bidirectionnels ainsi qu'un algorithme de tri qui fonctionne sur les itérateurs bidirectionnels.
Mise à jour : cela fonctionne pour les combinaisons de conteneurs de type séquence. Cependant, pour mélanger une liste, il faudrait que std::sort supporte les BidirectionalIterators (ce qui n'est pas le cas).
4 votes
Considérez comment
std::sort
réarrange les éléments : avecstd::iter_swap
. Votrezip_iterator
devrait être en mesure de le soutenir.2 votes
C'est une tâche très intéressante que vous vous êtes assignée :)
0 votes
Vous avez tort au sujet des itérateurs zip - vous pouvez compiler votre premier code si vous le faites correctement - mais il ne vous donnera pas de résultats corrects pour d'autres raisons (voir ici : stackoverflow.com/questions/9343846/ ). Cependant, ceci semble être plus ou moins ce que vous recherchez : stanford.edu/~dgleich/notebook/2006/03/ (Si je comprends bien ce qu'il fait, vous devrez peut-être ajouter d'autres valeurs PermuteIter (peut-être avec un tuple les contenant)).
1 votes
Voici le code d'Anthony Williams : tupleit.hh , testtupleit.cpp . Je l'ai trouvé dans tupleit.zip aquí (l'inscription est obligatoire).
0 votes
@interjay merci ! cela répond à la question !
0 votes
@JonathanWakely Puis-je aussi utiliser le zip de redi pour trier ? Les exemples montrent l'accès en lecture seule qui est déjà disponible avec
boost::zip_iterator
0 votes
@gnzlbg, non, désolé - j'ai oublié que l'opérateur de déréférencement de l'itérateur renvoie un proxy au lieu d'une référence, et qu'il n'est donc pas interchangeable ... c'est ma faute.
0 votes
Peut-être changer le tag boost en range-v3 ? (voir ma réponse)
0 votes
Voir aussi stackoverflow.com/q/45068782 (qui contient quelques discussions et réponses intéressantes)