2012-07-12 16 views
7

To jest (jeszcze) a (nother) śledzić odpowiedzi do Jamesa na to pytanie: Flattening iteratorJak spłaszczyć Iteratory kontenerów zagnieżdżonych?

Jak mogę zmienić flattenig_iterator taki, że działa rekurencyjnie? Powiedzmy, że mam więcej poziomów zagnieżdżonych kontenerów i nie chcę ograniczać się do danej głębokości zagnieżdżenia. To znaczy. flattening_iterator powinny pracować z

std::vector< std::vector < std::vector <int> > > 

jak również z

std::vector< std::vector < std::vector < std::vector <int> > > > 

W moim rzeczywisty kod mam tablicę obiektów, które mogą lub nie zawierać taką tablicę siebie.

edit:

Po zabawy z różnych sposobów iteracja różnego rodzaju zagnieżdżonych kontenerów nauczyłem się czegoś, co może być interesujące dla innych, a także:

Dostęp do elementów kontenera z zagnieżdżonych pętli wykonanych 5 do 6 razy szybciej niż w przypadku rozwiązania iteracyjnego.

Za:

  • elementy mogą być złożone, na przykład przedmiotów (jak w moim przypadku) klasy zawierające pojemniki.
  • szybsze wykonanie

Wady:

  • Każda struktura pojemnik wymaga nowego realizację pętli
  • standardowe algorytmy biblioteki nie są dostępne

Inne plusy i minusy?

Odpowiedz

4

OK, więc nie jest to pełne rozwiązanie - ale zabrakło mi czasu. Tak więc obecnie implementuje nie pełny iterator, ale wycinaną klasę podobną do iteratora, która definiuje coś podobnego do tego interfejsu i wymaga C++ 11. Przetestowałem go na g ++ 4.7:

template<typename NestedContainerType, typename Terminator> 
class flatten_iterator 
{ 
    bool complete(); 
    void advance(); 
    Terminator& current(); 
}; 

Gdzie NestedContainerType jest zagnieżdżony typ pojemnika (zaskakująco) i Terminator jest rodzaj najgłębszej rzeczy, że jesteś chcąc wydostać się z Spłaszcz.

Poniższy kod działa, ale z pewnością nie jest dokładnie przetestowany. Całkowite zawijanie (zakładając, że jesteś zadowolony tylko z wyprzedzania) nie powinno być zbyt wielkim wysiłkiem, w szczególności jeśli używasz boost::iterator_facade.

#include <list> 
#include <deque> 
#include <vector> 

#include <iostream> 

template<typename ContainerType, typename Terminator> 
class flatten_iterator 
{ 
public: 
    typedef flatten_iterator<typename ContainerType::value_type, Terminator> inner_it_type; 
    typedef typename inner_it_type::value_type value_type; 

public: 
    flatten_iterator() {} 

    flatten_iterator(ContainerType& container) : m_it(container.begin()), m_end(container.end()) 
    { 
     skipEmpties(); 
    } 

    bool complete() 
    { 
     return m_it == m_end; 
    } 

    value_type& current() 
    { 
     return m_inner_it.current(); 
    } 

    void advance() 
    { 
     if (!m_inner_it.complete()) 
     { 
      m_inner_it.advance(); 
     } 
     if (m_inner_it.complete()) 
     { 
      ++m_it; 
      skipEmpties(); 
     } 
    } 

private: 
    void skipEmpties() 
    { 
     while (!complete()) 
     { 
      m_inner_it = inner_it_type(*m_it); 
      if (!m_inner_it.complete()) break; 
      ++m_it; 
     } 
    } 

private: 
    inner_it_type     m_inner_it; 
    typename ContainerType::iterator m_it, m_end; 
}; 


template<template<typename, typename ...> class ContainerType, typename Terminator, typename ... Args> 
class flatten_iterator<ContainerType<Terminator, Args...>, Terminator> 
{ 
public: 
    typedef typename ContainerType<Terminator, Args...>::value_type value_type; 

public: 
    flatten_iterator() {} 

    flatten_iterator(ContainerType<Terminator, Args...>& container) : 
     m_it(container.begin()), m_end(container.end()) 
    { 
    } 

    bool complete() 
    { 
     return m_it == m_end; 
    } 

    value_type& current() { return *m_it; } 
    void advance() { ++m_it; } 

private: 
    typename ContainerType<Terminator, Args...>::iterator m_it, m_end; 
}; 

I z następujących przypadków testowych, to nie to, czego można się spodziewać:

int main(int argc, char* argv[]) 
{ 
    typedef std::vector<int> n1_t; 
    typedef std::vector<std::deque<short> > n2_t; 
    typedef std::list<std::vector<std::vector<std::vector<double> > > > n4_t; 
    typedef std::vector<std::deque<std::vector<std::deque<std::vector<std::list<float> > > > > > n6_t; 

    n1_t n1 = { 1, 2, 3, 4 }; 
    n2_t n2 = { {}, { 1, 2 }, {3}, {}, {4}, {}, {} }; 
    n4_t n4 = { { { {1.0}, {}, {}, {2.0}, {} }, { {}, {} }, { {3.0} } }, { { { 4.0 } } } }; 
    n6_t n6 = { { { { { {1.0f}, {}, {}, {2.0f}, {} }, { {}, {} }, { {3.0f} } }, { { { 4.0f } } } } } }; 

    flatten_iterator<n1_t, int> i1(n1); 
    while (!i1.complete()) 
    { 
     std::cout << i1.current() << std::endl; 
     i1.advance(); 
    } 

    flatten_iterator<n2_t, short> i2(n2); 
    while (!i2.complete()) 
    { 
     std::cout << i2.current() << std::endl; 
     i2.advance(); 
    } 

    flatten_iterator<n4_t, double> i4(n4); 
    while (!i4.complete()) 
    { 
     std::cout << i4.current() << std::endl; 
     i4.advance(); 
    } 

    flatten_iterator<n6_t, float> i6(n6); 
    while (!i6.complete()) 
    { 
     std::cout << i6.current() << std::endl; 
     i6.advance(); 
    } 
} 

Więc drukuje następujące informacje dla każdego z typów kontenerów:

1 
2 
3 
4 

pamiętać, że nie robi 't jeszcze pracować z set s, ponieważ istnieje kilka foo wymagane do radzenia sobie z faktem, że Iteratory set zwracają odwołania stałych. Ćwiczenie dla czytelnika ... :-)

+0

wow. wygląda dobrze, działa, bardzo blisko tego, czego potrzebuję. Jedna uwaga: staram się używać tak mało bibliotek, jak to konieczne. Czy 'boost :: scoped_ptr' jest naprawdę potrzebny? – steffen

+1

'scoped_ptr' nie jest całkowicie konieczne. Po prostu przechowuj iterator według wartości. –

+0

??? Zgaduję, że robię głupi błąd, ale wiersz 'nazwa_pliku inner_it_type m_inner_it;' daje błąd kompilatora 'oczekiwany specyfikator nazwy zagnieżdżonej przed 'inner_it_type'' – steffen

7

będę szybko przedstawić rozwiązania:

  1. Napisz do is_container cechę, która wykrywa begin() i end() członków, ewentualnie jakieś bardziej skomplikowane zasady;
  2. Napisz szablon all_flattening_iterator<T>, który jest tylko flattening_iterator<all_flattening_iterator<typename T::value_type>>;
  3. Napisz specjalizację all_flattening_iterator<T>, gdy T nie jest kontenerem (użyj domyślnego parametru szablonu bool), który jest zwykłym iteratorem.
+0

Prawdopodobnie szablony variadyczne mogą zapewnić wygodniejszy sposób użycia proponowanej metafunkcji 'is_container'. – xtofl

+0

@xtofl jak przydatne są szablony variadyczne? Jest tylko jeden parametr szablonu. –

+0

Marzyłem o sposobie użycia 'list' i' dequeue' oraz wszystkiego z 'begin' i' end' za jednym zamachem :) – xtofl

0

Przyjeżdżam trochę późno, ale właśnie opublikowałem a library (multidim), aby poradzić sobie z takim problemem. Aby uzyskać szczegółowe informacje, sprawdź numer my answer to the related question.

Moje rozwiązanie jest inspirowane przez Alex Wilson's idea korzystania z iteratorów "zagnieżdżonych teleskopowo". Dodaje jednak trochę więcej funkcjonalności (na przykład obsługa kontenera tylko do odczytu, takiego jak set s, iteracja wsteczna, dostęp losowy) i oferuje bardziej przyjemny interfejs, ponieważ automatycznie wykrywa typ elementów "liścia".

Powiązane problemy