2009-06-11 18 views
22

itertools di Python implementano un iteratore chain che concatena essenzialmente diversi iteratori per fornire tutto da singolo iteratore.concatenamento iteratori per C++

C'è qualcosa di simile in C++? Un rapido sguardo alle librerie di boost non ha rivelato qualcosa di simile, il che è abbastanza sorprendente per me. È difficile implementare questa funzionalità?

+0

ho trovato questo: http://echochamber.me/viewtopic.php?f=11&t=19074, che fa qualcosa di simile, anche se non come generico come vorrei. – ynimous

risposta

18

Si è imbattuto in questa domanda durante l'analisi per un problema simile.

Anche se la domanda è vecchia, ora al tempo di C++ 11 e 1.54 di spinta è abbastanza facile da fare utilizzando la libreria Boost.Range. È dotato di un join-function, che può unire due intervalli in uno solo. Qui potresti incorrere in penalizzazioni prestazionali, poiché il concetto di intervallo comune più basso (ovvero Single Pass Range or Forward Range ecc.) Viene utilizzato come categoria della nuova gamma e durante l'iterazione l'iteratore potrebbe essere controllato se è necessario saltare al nuovo intervallo, ma il codice può essere facilmente scritta come:

#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

È possibile fare a meno dell'enfasi? –

1

Non nella libreria standard. Boost potrebbe avere qualcosa.

Ma in realtà, una cosa del genere dovrebbe essere banale da implementare. Crea un iteratore con un vettore di iteratori come membro. Un codice molto semplice per operatore ++, e tu sei lì.

+0

Dovrebbero essere coppie di iteratori - è necessario sapere dove si ferma ciascuno. –

4

In C++, un iteratore di solito non ha senso al di fuori di un contesto di inizio e fine di un intervallo. Lo stesso iteratore non sa dove sono l'inizio e la fine. Quindi, per fare qualcosa di simile, è necessario invece concatenare intervalli di iteratori - l'intervallo è una coppia (iniziale, finale) di iteratori.

prende in esame la documentazione boost::range. Può fornire strumenti per costruire una catena di intervalli. L'unica differenza è che dovranno essere dello stesso tipo e restituire lo stesso tipo di iteratore. Potrebbe inoltre essere possibile rendere questo ulteriore generico per concatenare diversi tipi di intervalli con qualcosa come any_iterator, ma forse no.

2

ho scritto uno prima (in realtà, solo per la catena due coppie di iteratori insieme). Non è così difficile, soprattutto se usi boost iterator_facade.

Fare un iteratore di input (che è in effetti ciò che fa Python chain) è un primo passo facile. Trovare la categoria corretta per un iteratore che concatena una combinazione di diverse categorie di iteratori è lasciato come esercizio per il lettore ;-).

+0

Il concatenamento di tre iteratori insieme è banale per iterazione: ((A, B), C) – MSalters

0

No funzionalità esiste in spinta che implementa questa, al meglio della mia conoscenza - ho fatto una bella lunga ricerca.

Ho pensato di implementarlo facilmente la scorsa settimana, ma ho avuto un problema: il file STL fornito con Visual Studio 2008, quando il controllo intervallo è attivo, non consente di confrontare gli iteratori da diversi contenitori (ad es. non è possibile confrontare somevec1.end() con somevec2.end()). Tutto ad un tratto è diventato molto più difficile implementarlo e non ho ancora deciso su come farlo.

Ho scritto altri iteratori in passato usando iterator_facade e iterator_adapter di boost, che sono migliori della scrittura di iteratori "grezzi" ma trovo ancora la scrittura di iteratori personalizzati in C++ piuttosto disordinati.

Se qualcuno può postare qualche pseudocodice su come ciò potrebbe essere fatto/senza/confrontando iteratori da contenitori diversi, sarei molto grato.

+0

Nessun STL lo consente, in realtà. VS2008 ti dice solo prima. Ma il design dovrebbe consentire di concatenare un iteratore vettoriale e un iteratore di elenchi, nel qual caso un confronto sarebbe comunque un errore di tipo. – MSalters

1

Controllare Views Template Library (VTL). Potrebbe non fornire direttamente 'iteratore concatenato'. Ma penso che abbia tutti gli strumenti/modelli necessari per implementare il tuo 'iteratore incatenato'.


Dal VTL Pagina:

Un punto di vista è un adattatore contenitore, che fornisce un'interfaccia contenitore per

  1. parti dei dati o
  2. un riarrangiamento dei dati o
  3. dati trasformati o
  4. una combinazione adeguata dei set di dati

del/i contenitore/i sottostante/i. Poiché le viste stesse forniscono l'interfaccia del contenitore, possono essere facilmente combinate e impilate. A causa del modello di trucco, le visualizzazioni possono adattare la loro interfaccia ai contenitori sottostanti. Trucchi di modelli più sofisticati rendono questa potente funzionalità facile da usare.

Rispetto agli iteratori intelligenti, le visualizzazioni sono solo fabbriche iteratrici intelligenti.

2

Quello che stai cercando in sostanza è un iteratore di facciata che astrae il passaggio attraverso diverse sequenze.

Dato che vieni da uno sfondo pitone, suppongo che ti preoccupi di più della flessibilità piuttosto che della velocità. Per flessibilità intendo la possibilità di eseguire un'iterazione a catena di diversi tipi di sequenza (vettore, array, elenco collegato, set ecc.) E per velocità intendo solo l'allocazione della memoria dallo stack.

Se questo è il caso, allora si consiglia di guardare l'any_iterator dai laboratori di Adobe: http://stlab.adobe.com/classadobe_1_1any__iterator.html

Questo iteratore vi darà la possibilità di scorrere qualsiasi tipo di sequenza in fase di esecuzione. Per concatenare si dovrebbe avere un vettore (o array) di 3-tupla any_iterators, cioè tre any_iterators per ogni intervallo che si incatena (ne occorrono tre per scorrere avanti o indietro, se si desidera solo iterare due avanti sarà sufficiente).

Diciamo che si voleva catena iterate attraverso una sequenza di numeri interi:

(codice non testato pseudo-C++)

typedef adobe :: any_iterator AnyIntIter;

struct AnyRange { AnyIntIter begin; AnyIntIter curr; Fine di AnyIntIter; };

Si potrebbe definire un intervallo come ad esempio:

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

La classe RangeIterator avrebbe quindi un vettore std ::.

<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> 

Desidero notare tuttavia che questa soluzione è molto lenta. L'approccio migliore, più simile a C++, è solo quello di archiviare tutti i puntatori agli oggetti su cui vuoi operare e iterare attraverso quello. In alternativa, puoi applicare un funtore o un visitatore ai tuoi intervalli.