2012-07-12 74 views
7

này (chưa) một (Nother) theo dõi để James' câu trả lời cho câu hỏi này: Flattening iteratorLàm thế nào để làm phẳng các vòng lặp của các vùng chứa lồng nhau?

Làm thế nào để thay đổi flattenig_iterator như vậy mà nó làm việc một cách đệ quy? Nói rằng tôi có nhiều cấp độ của các thùng chứa lồng nhau và tôi không muốn bị giới hạn ở độ sâu lồng nhau nhất định. I E. flattening_iterator nên làm việc với

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

cũng như với

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

Trong mã thực tế của tôi, tôi có một mảng của các đối tượng mà có thể hoặc không chứa một mảng như vậy bản thân.

chỉnh sửa:

Sau khi chơi xung quanh với nhiều cách khác nhau của iterating qua loại khác nhau của container lồng nhau Tôi đã học được một cái gì đó có thể là thú vị cho những người khác cũng như:

Truy cập vào các yếu tố container với vòng lồng nhau thực hiện 5 đến 6 lần nhanh hơn so với giải pháp lặp.

Ưu điểm:

  • yếu tố có thể đối tượng phức tạp, ví dụ (như trong trường hợp của tôi) các lớp học có chứa các thùng chứa.
  • nhanh thực hiện

Nhược điểm:

  • Mỗi cấu trúc chứa đòi hỏi một thực hiện mới của vòng lặp
  • thư viện các thuật toán tiêu chuẩn không có sẵn

ưu và nhược điểm khác?

Trả lời

4

Ok, vì vậy đây không phải là giải pháp đầy đủ - nhưng tôi hết thời gian. Vì vậy, điều này hiện đang thực hiện không phải là một iterator đầy đủ nhưng một lớp học giảm giống như iterator mà định nghĩa một cái gì đó giống như giao diện này, và yêu cầu C + + 11. Tôi đã thử nghiệm nó trên g ++ 4.7:

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

đâu NestedContainerType là loại chứa lồng nhau (ngạc nhiên), và Terminator là loại điều sâu thẳm nhất mà bạn đang mong muốn có được ra khỏi flatten.

Mã bên dưới hoạt động, nhưng điều này chắc chắn không được thử nghiệm rộng rãi. Gói nó lên đầy đủ (giả sử bạn đang hạnh phúc với tiến lên phía trước chỉ) không nên quá nhiều công việc, đặc biệt nếu bạn sử dụng 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; 
}; 

Và với trường hợp kiểm tra sau, nó làm những gì bạn mong đợi:

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(); 
    } 
} 

Vì vậy, in ra sau cho mỗi người trong số các loại container:

1 
2 
3 
4 

Lưu ý rằng nó doesn chưa làm việc với set s vì có một số foo cần thiết để đối phó với thực tế là các trình lặp vòng set trả về tham chiếu const. Tập thể dục cho người đọc ... :-)

+0

wow. có vẻ tốt, công trình, rất gần với những gì tôi cần. Một nhận xét: Tôi cố gắng sử dụng ít thư viện khi cần thiết. Vì vậy, là 'boost :: scoped_ptr' thực sự cần thiết? – steffen

+1

'scoped_ptr' hoàn toàn không cần thiết. Chỉ lưu trữ trình vòng lặp theo giá trị. –

+0

??? Tôi đoán tôi đang tạo ra một sai lầm ngu ngốc, nhưng dòng 'typename inner_it_type m_inner_it;' cung cấp cho trình biên dịch lỗi 'dự ​​kiến ​​nested-name-specifier trước 'inner_it_type'' – steffen

7

tôi sẽ nhanh chóng phác thảo một giải pháp:

  1. Viết một đặc điểm is_container phát hiện begin()end() thành viên, hoặc có thể là một số quy tắc phức tạp hơn;
  2. Viết một mẫu all_flattening_iterator<T> chỉ là flattening_iterator<all_flattening_iterator<typename T::value_type>>;
  3. Viết chuyên môn của all_flattening_iterator<T> cho khi T không phải là vùng chứa (sử dụng tham số mẫu mặc định bool) chỉ là một trình lặp thường xuyên.
+0

có lẽ mẫu variadic có thể cung cấp một cách thuận tiện hơn để sử dụng tính năng thay thế 'is_container' được đề xuất. – xtofl

+0

@xtofl mẫu variadic hữu ích ở đây như thế nào? Chỉ có một tham số mẫu có liên quan. –

+0

Tôi đã mơ về một cách để sử dụng 'list' và' dequeue' và mọi thứ với 'begin' và' end' trong một lần :) – xtofl

0

Tôi đến trễ một chút ở đây, nhưng tôi vừa xuất bản a library (multidim) để giải quyết vấn đề như vậy. Hãy xem my answer to the related question để biết chi tiết.

Giải pháp của tôi lấy cảm hứng từ Alex Wilson's idea bằng cách sử dụng trình lặp vòng lặp "lồng nhau". Tuy nhiên, chức năng này bổ sung thêm một số chức năng (ví dụ: hỗ trợ cho vùng chứa chỉ đọc chẳng hạn như set s, lặp lại ngược, truy cập ngẫu nhiên) và cung cấp giao diện dễ chịu hơn vì nó tự động phát hiện loại phần tử "lá".

Các vấn đề liên quan