2012-07-12 21 views
7

(अभी तक) एक (nother) इस सवाल का जेम्स 'जवाब देने के लिए अनुवर्ती यह है: Flattening iteratorनेस्टेड कंटेनरों के इटरेटर को कैसे फ़्लैट करें?

मैं कैसे flattenig_iterator ऐसी है कि वह रिकर्सिवली काम करता है बदल सकता हूँ? मान लें कि मेरे पास नेस्टेड कंटेनर के अधिक स्तर हैं और मैं किसी दिए गए घोंसले की गहराई तक सीमित नहीं होना चाहता हूं। अर्थात। flattening_iterator

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

साथ के साथ ही

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

साथ काम करना चाहिए अपने वास्तविक कोड में मैं जिन वस्तुओं पर या इस तरह के एक सरणी के लिए खुद को शामिल नहीं हो सकता है की एक सरणी है।

संपादित:

निष्पादित नेस्टेड छोरों के साथ कंटेनर तत्वों को एक्सेस करना:

नेस्टेड कंटेनर मैं कुछ है कि दूसरों के लिए दिलचस्प रूप में अच्छी तरह हो सकता है सीखा के विभिन्न प्रकार के माध्यम से पुनरावृत्ति के विभिन्न तरीकों के साथ चारों ओर खेलने के बाद इटरेटर समाधान के मुकाबले 5 से 6 गुना तेज।

सकारात्मक:

  • तत्वों जटिल वस्तुओं, जैसे हो सकता है (मेरे मामले में) कक्षाएं जिसमें कंटेनर होते हैं।
  • तेजी से निष्पादन

विपक्ष:

  • प्रत्येक कंटेनर संरचना पाश की एक नई कार्यान्वयन की आवश्यकता है
  • मानक पुस्तकालय एल्गोरिदम उपलब्ध

अन्य पक्ष-विपक्ष नहीं कर रहे हैं?

उत्तर

4

ठीक है, तो यह एक पूर्ण समाधान नहीं है - लेकिन मैं समय से बाहर भाग गया। तो यह वर्तमान में पूर्ण इटरेटर नहीं लागू करता है लेकिन इटरेटर जैसी कक्षा को काटता है जो इस इंटरफेस की तरह कुछ परिभाषित करता है, और सी ++ 11 की आवश्यकता होती है। मैं जी ++ 4.7 पर जाँच की है:

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

कहाँ NestedContainerType नेस्टेड कंटेनर प्रकार (आश्चर्यजनक) है, और टर्मिनेटर अंतरतम बात यह है कि आप समतल से बाहर निकलने के लिए इच्छुक रहे हैं के प्रकार है।

नीचे दिया गया कोड काम करता है, लेकिन यह निश्चित रूप से व्यापक रूप से परीक्षण नहीं किया जाता है। इसे पूरी तरह से लपेटना (माना जाता है कि आप केवल अग्रिम अग्रिम के साथ खुश हैं) विशेष रूप से यदि आप boost::iterator_facade का उपयोग करते हैं तो बहुत अधिक काम नहीं होना चाहिए।

1 
2 
3 
4 

ध्यान दें कि यह नहीं करता है:

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

तो कंटेनर प्रकारों में से प्रत्येक के लिए निम्नलिखित प्रिंट:

#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; 
}; 

और निम्न परीक्षण मामलों के साथ, यह आप क्या उम्मीद करेंगे करता है set एस के साथ अभी तक काम नहीं कर रहा है क्योंकि इस तथ्य से निपटने के लिए कुछ फू आवश्यक है कि set इटरेटर्स रिटर्न कॉन्स्ट्रेंस संदर्भ। पाठक के लिए व्यायाम ... :-)

+0

वाह। अच्छा लग रहा है, काम करता है, मुझे जो चाहिए वह बहुत करीब है। एक टिप्पणी: मैं आवश्यकतानुसार छोटी पुस्तकालयों के रूप में उपयोग करने की कोशिश करता हूं। तो क्या 'boost :: scoped_ptr' वास्तव में आवश्यक है? – steffen

+1

'scoped_ptr' पूरी तरह से आवश्यक नहीं है। बस इटरेटर को मूल्य से स्टोर करें। –

+0

??? मुझे लगता है कि मैं एक बेवकूफ गलती कर रहा हूं, लेकिन लाइन 'typename inner_it_type m_inner_it' 'कंपाइलर त्रुटि' को 'आंतरिक_ट_टाइप' – steffen

7

मैं जल्दी से एक समाधान की रूपरेखा तैयार करेंगे:

  1. एक is_container ऐसी विशेषता है जो begin() और end() के सदस्यों, या शायद कुछ और अधिक जटिल नियमों का पता लगाता है लिखें;
  2. all_flattening_iterator<T> टेम्पलेट लिखें जो केवल flattening_iterator<all_flattening_iterator<typename T::value_type>> है;
  3. all_flattening_iterator<T> का एक विशेषज्ञता लिखें जब T कंटेनर नहीं है (डिफ़ॉल्ट टेम्पलेट bool पैरामीटर का उपयोग करें) जो कि केवल एक नियमित पुनरावर्तक है।
+0

शायद विविधतापूर्ण टेम्पलेट प्रस्तावित 'is_container' मेटाफंक्शन का उपयोग करने का एक अधिक सुविधाजनक तरीका प्रदान कर सकते हैं। – xtofl

+0

@xtofl वैरिएड टेम्पलेट्स यहां सहायक कैसे हैं? इसमें केवल एक टेम्पलेट पैरामीटर शामिल है। –

+0

मैं 'सूची' और 'डेक्यू' का उपयोग करने के लिए एक तरीका का सपना देख रहा था और एक ही समय में 'स्टार्ट' और 'एंड' के साथ सब कुछ :) – xtofl

0

मैं यहां कुछ देर से पहुंचे, लेकिन मैंने इस समस्या से निपटने के लिए अभी a library (multidim) प्रकाशित किया है। विवरण के लिए my answer to the related question देखें।

मेरा समाधान "टेलीस्कोपिकली नेस्टेड" इटरेटर्स का उपयोग करने के Alex Wilson's idea से प्रेरणा लेता है। हालांकि यह कुछ और कार्यक्षमता जोड़ता है (उदाहरण के लिए केवल पढ़ने के लिए कंटेनर जैसे set एस, पीछे की ओर इशारा करते हुए, यादृच्छिक पहुंच) और अधिक सुखद इंटरफ़ेस प्रदान करता है, क्योंकि यह "पत्ते" तत्वों के प्रकार को स्वतः-पहचानता है।