2009-06-11 6 views
22

Les outils IT de Python implémentent un itérateur chain qui concatène essentiellement un certain nombre d'itérateurs différents pour tout fournir à partir d'un seul itérateur.Les itérateurs de chaînage pour C++

Y at-il quelque chose de similaire en C++? Un rapide coup d'œil sur les bibliothèques de boost n'a pas révélé quelque chose de similaire, ce qui est assez surprenant pour moi. Est-ce difficile d'implémenter cette fonctionnalité?

+0

J'ai trouvé ceci: http://echochamber.me/viewtopic.php?f=11&t=19074, qui fait quelque chose de similaire, mais pas aussi générique que je voudrais. – ynimous

Répondre

18

Entré à travers cette question tout en recherchant un problème similaire.

Même si la question est ancienne, maintenant au moment de C++ 11 et boost 1.54, il est assez facile à faire en utilisant la bibliothèque Boost.Range. Il dispose d'un join-function, qui peut joindre deux plages en une seule. Vous pouvez encourir des pénalités de performance, car le concept de plage commune le plus bas (Single Pass Range or Forward Range etc.) est utilisé comme catégorie de nouvelle plage et pendant l'itération, l'itérateur peut être vérifié s'il doit passer à la nouvelle plage, mais votre code peut être facilement écrit comme:

#include <boost/range/join.hpp> 

#include <iostream> 
#include <vector> 
#include <deque> 

int main() 
{ 
    std::deque<int> deq = {0,1,2,3,4}; 
    std::vector<int> vec = {5,6,7,8,9}; 

    for(auto i : boost::join(deq,vec)) 
    std::cout << "i is: " << i << std::endl; 

    return 0; 
} 
+7

Est-ce possible de faire sans boost? –

1

Pas dans la bibliothèque standard. Boost pourrait avoir quelque chose.

Mais vraiment, une telle chose devrait être triviale à mettre en œuvre. Fais juste un itérateur avec un vecteur d'itérateurs en tant que membre. Un code très simple pour l'opérateur ++, et vous êtes là.

+0

Il devrait s'agir de paires d'itérateurs - vous devez savoir où chacun s'arrête. –

4

En C++, un itérateur n'a généralement aucun sens en dehors du contexte du début et de la fin d'une plage. L'itérateur lui-même ne sait pas où sont le début et la fin. Donc, pour faire quelque chose comme ça, vous devez plutôt enchaîner des plages d'itérateurs - range est une paire (début, fin) d'itérateurs.

Examine la documentation boost::range. Il peut fournir des outils pour construire une chaîne de gammes. La seule différence est qu'ils devront être du même type et retourner le même type d'itérateur. Il peut en outre être possible de rendre cela plus générique pour enchaîner différents types de plages avec quelque chose comme any_iterator, mais peut-être pas.

2

J'en ai déjà écrit un (en fait, juste pour enchaîner deux paires d'itérateurs). Ce n'est pas si difficile, surtout si vous utilisez iterator_facade boost.

Faire un itérateur d'entrée (ce que fait effectivement le chain de Python) est une première étape facile. Trouver la catégorie correcte pour un itérateur enchaînant une combinaison de différentes catégories d'itérateur est laissé comme un exercice pour le lecteur ;-).

+0

L'enchaînement de trois itérateurs est trivial par itération: ((A, B), C) – MSalters

0

Aucune fonctionnalité n'existe dans boost qui implémente ceci, à ma connaissance - j'ai fait une recherche assez complète. Je pensais pouvoir l'implémenter facilement la semaine dernière, mais je suis tombé sur un hic: la STL fournie avec Visual Studio 2008, lorsque la vérification de la gamme est activée, ne permet pas de comparer les itérateurs de différents conteneurs (c.-à-d. ne peut pas comparer somevec1.end() avec somevec2.end()). Tout d'un coup, il est devenu beaucoup plus difficile de mettre en œuvre cela et je n'ai pas encore tout à fait décidé comment le faire.

j'ai écrit d'autres itérateurs dans le passé en utilisant iterator_facade et iterator_adapter de boost, qui sont mieux que d'écrire itérateurs « bruts », mais je trouve encore à écrire itérateurs personnalisés en C++ plutôt en désordre.

Si quelqu'un peut poster un pseudo-code sur la façon dont cela pourrait être fait/sans/comparer les itérateurs de différents conteneurs, je serais très obligé.

+0

Aucune STL ne le permet réellement. VS2008 vous le dit plus tôt. Mais la conception devrait permettre de chaîner un itérateur vectoriel et un itérateur de liste, auquel cas une comparaison serait de toute façon une erreur de type. – MSalters

1

Vérifiez Views Template Library (VTL). Il peut ne pas fournir 'itérateur chaîné' directement. Mais je pense qu'il dispose de tous les outils/modèles nécessaires pour implémenter votre propre "itérateur chaîné".


De l'VTL Page:

Une vue est un adaptateur de conteneur, qui fournit une interface de conteneur à

  1. parties des données ou
  2. un réarrangement des données ou
  3. données transformées ou
  4. une combinaison appropriée des ensembles de données

du (des) conteneur (s) sous-jacent (s). Puisque les vues fournissent elles-mêmes l'interface du conteneur, elles peuvent être facilement combinées et empilées. En raison de la supercherie de gabarit, les vues peuvent adapter leur interface au (x) conteneur (s) sous-jacent (s). Une supercherie de gabarits plus sophistiquée rend cette puissante fonctionnalité facile à utiliser.

Comparées aux itérateurs intelligents, les vues ne sont que des usines d'itération intelligentes.

2

Ce que vous recherchez essentiellement, c'est un itérateur de façade qui permet d'abstraire la traversée à travers plusieurs séquences.

Puisque vous venez d'un environnement python, je suppose que vous vous souciez plus de la flexibilité que de la vitesse. Par flexibilité, j'entends la capacité d'enchaîner les différents types de séquences (vecteur, tableau, liste chaînée, ensemble, etc ...) et par vitesse je veux seulement allouer de la mémoire à partir de la pile.

Si tel est le cas, alors vous voudrez peut-être regarder le any_iterator des laboratoires d'adobe: http://stlab.adobe.com/classadobe_1_1any__iterator.html

Ce iterator vous donnera la possibilité de parcourir tout type de séquence lors de l'exécution. Pour enchaîner vous auriez un vecteur (ou un tableau) de 3 tuple any_iterators, c'est-à-dire trois any_iterators pour chaque chaîne que vous enchaînez (vous avez besoin de trois pour itérer vers l'avant ou vers l'arrière, si vous voulez itérer vers l'avant deux suffiront).

Disons que vous vouliez chaîne itérer par une suite d'entiers:

(c-Untested psuedo Code ++)

typedef adobe :: any_iterator AnyIntIter; Struct anyRange { AnyIntIter begin; AnyIntIter curr; Fin AnyIntIter; };

Vous pouvez définir une plage telle que:

int int_array [] = {1, 2, 3, 4}; AnyRange sequence_0 = {int_array, int_array, int_array + ARRAYSIZE (int_array)};

Votre classe RangeIterator aurait alors un vecteur std :: vector.

<code> 
class RangeIterator { 
public: 
    RangeIterator() : curr_range_index(0) {} 

    template <typename Container> 
    void AddAnyRange(Container& c) { 
    AnyRange any_range = { c.begin(), c.begin(), c.end() }; 
    ranges.push_back(any_range); 
    } 

    // Here's what the operator++() looks like, everything else omitted. 
    int operator++() { 

    while (true) { 
     if (curr_range_index > ranges.size()) { 
     assert(false, "iterated too far"); 
     return 0; 
     } 
     AnyRange* any_range = ranges[curr_range_index]; 
     if (curr_range->curr != curr_range->end()) { 
     ++(curr_range->curr); 
     return *(curr_range->curr); 
     } 
     ++curr_range_index;  
    } 
    } 


private: 
    std::vector<AnyRange> ranges; 
    int curr_range_index; 
}; 
</code> 

Je tiens à noter cependant que cette solution est très lente. La meilleure approche de type C++ consiste simplement à stocker tous les pointeurs vers les objets sur lesquels vous voulez opérer et à parcourir. Alternativement, vous pouvez appliquer un foncteur ou un visiteur à vos plages.