2012-03-30 13 views
11

N तत्व v = (1, 2, 3, 4, ... , N) के वेक्टर को K<N आकार के सभी हिस्सों पर रिटर्न रेंज इटरेटर दिया गया है। K से N%K!=0 की तुलना में अंतिम सीमा छोटी हो सकती है।कंटेनर को टुकड़ों में विभाजित करें, सी ++

उदाहरण के लिए:

v = ("a","b","c","d","e") 

प्रदर्शन तार

"ab", "cd", "e" 

N=v.size(); 
K=2; 

एक संभव समाधान है:

for(unsigned int i=0; i<v.size(); i+=K) 
    cout << boost::join(v | boost::adaptors::sliced(i, min(i+K, v.size())), ""); 

यह समाधान काफी ठीक है, लेकिन यह कई समस्याएं हैं:

  1. for पाश - क्या इसकी आवश्यकता है?
  2. यदि आप min(i+K, v.size()) एल्गोरिदम क्रश के बजाय लिखते हैं, तो किसी को सीमा के मामले पर अतिरिक्त ध्यान देना होगा। यह बदसूरत और परेशान दिखता है।

क्या आप अधिक सुरुचिपूर्ण समाधान का प्रस्ताव दे सकते हैं? सुरुचिपूर्ण समाधान से मेरा मतलब है कि सामान्य एल्गोरिदम का उपयोग, आमतौर पर उपयोग की जाने वाली लाइब्रेरी (जैसे बूस्ट) द्वारा निर्मित या प्रदान किया जाता है।

-------------------------- [संपादित करें] ----------------- ---------

आप में से कुछ ने काम करने का उदाहरण दिया, यहां यह है।

#include <iostream> 
#include <vector> 
#include <string> 
#include <boost/range/adaptor/sliced.hpp> 
#include <boost/algorithm/string/join.hpp> 
#include <boost/assign.hpp> //just for fun 

using namespace std; 
using namespace boost::assign; 

int main(int , char **) 
{ 
    const int K = 2; 
    vector<string> v; 
    v += "a","b","c","d","e"; 

    for(unsigned int i=0; i<v.size(); i+=K) 
     cout << boost::algorithm::join( 
        v | boost::adaptors::sliced(i, min(i+K, v.size())), "") 
      << endl; 
} 

आउटपुट:

template <class T, class Func> 
void do_chunks(T container, size_t K, Func func) { 
    size_t size = container.size(); 
    size_t i = 0; 

    // do we have more than one chunk? 
    if (size > K) { 
     // handle all but the last chunk 
     for (; i < size - K; i += K) { 
      func(container, i, i + K); 
     } 
    } 

    // if we still have a part of a chunk left, handle it 
    if (i % K) { 
     func(container, i, i + i % K); 
    } 
} 
+0

आप पूर्ण उदाहरण क्यों नहीं पोस्ट करते? उदाहरण के लिए –

+1

@VJovic मैंने दिखाया कि मुझे वास्तव में क्या चाहिए लेकिन यह एक सामान्य प्रश्न है, एक कंटेनर के प्रत्येक हिस्से पर अलग-अलग एल्गोरिदम कैसे चलाएं। – bartek

+0

दुर्भाग्यवश, मैं आपके उदाहरण को संकलित नहीं कर सकता, और मैंने अपनी क्रिस्टल बॉल खो दी;) –

उत्तर

6

इस अच्छे प्रदर्शन के साथ एक प्रकार के- सामान्य समाधान है मानक कार्यों के साथ इटरेटर अग्रिम और दूरी:

template<typename Iterator, typename Func, typename Distance> 
void chunks(Iterator begin, Iterator end, Distance k ,Func f){ 
    Iterator chunk_begin; 
    Iterator chunk_end; 
    chunk_end = chunk_begin = begin; 

    do{ 
     if(std::distance(chunk_end, end) < k) 
      chunk_end = end; 
     else 
      std::advance(chunk_end, k); 
     f(chunk_begin,chunk_end); 
     chunk_begin = chunk_end; 
    }while(std::distance(chunk_begin,end) > 0); 
} 
+0

क्या मैं इसे सामान्य एल्गोरिदम का उपयोग कर कर सकता हूं? – bartek

+2

@ बार्टेक: मुझे लगता है कि यहां बिंदु यह है कि यह एक सामान्य एल्गोरिदम बन जाता है जिसे आप एक ही तर्क को दोहराने के बजाय अपने पूरे कोड का उपयोग कर सकते हैं। –

+0

@VaughnCato जब इस तरह के एल्गोरिदम लिखते हैं तो 'एडेप्टर :: कटा हुआ' में बस 'मिनट' फ़ंक्शन को भूलने से अधिक गलतियां कर सकते हैं। यही कारण है कि मैंने ऐसे समाधान के लिए कहा जो मेरे उदाहरण में सामान्य जेनरेट एल्गोरिदम का उपयोग करता है। – bartek

8

मैं अगर यह बहुत सुंदर है पता नहीं है, लेकिन आप उपयोग कर सकते हैं:

ab 
cd 
e 
+1

+1। फिर भी, 'if (std :: distance (chunk_end, end)> k) 'भाग काफी विचलित है। मेरे समाधान कम हैं लेकिन आपका बाहरी पुस्तकालय का उपयोग नहीं करता है। – bartek

+0

लाइन नहीं होना चाहिए (std :: दूरी (chunk_end, end)> k) वास्तव में PBJ

+0

@ डेविड हाँ आप सही हैं! आपके जवाब के लिए – BenjaminB

8

डब्लूआरटी "लूप के लिए इसकी आवश्यकता है?"

यदि कोई std::distance() का उपयोग करने से बचना चाहता है तो एक लूप निर्माण की आवश्यकता होती है क्योंकि किसी को ट्रैक करने की आवश्यकता होती है कि कितना बचा है। (रैंडम एक्सेस कंटेनरों के लिए, std::distance() सस्ती है --but अन्य सभी के लिए यह इस एल्गोरिथ्म के लिए बहुत महंगी है।)

WRT मैं + K/मिनट() मुद्दा

न लिखें मैं K + या कुछ भी जो रैपिंग/ओवर/अंडरफ्लो मुद्दों का कारण बन सकता है। इसके बजाय ट्रैक करें कि कितना बचा है और घटाना है। इसे min() का उपयोग करने की आवश्यकता होगी।

WRT सुरुचिपूर्ण समाधान

इस एल्गोरिथ्म अधिक "सुंदर" उस में है:

  1. पहले दो "WRT" ऊपर आइटम मुद्दे नहीं हैं।
  2. यह किसी बाहरी पुस्तकालय का उपयोग नहीं करता है; --it केवल std::copy_n() और std::advance() का उपयोग करता है।
  3. यदि यह आवश्यक/वांछित है तो यह तर्क-निर्भर लुकअप का फायदा उठाता है।
  4. यह कंटेनर का size_type का उपयोग करता है।
  5. यह किसी भी कंटेनर के साथ कुशलतापूर्वक काम करेगा।
  6. यदि के < = 0 तो std::domain_error फेंक दिया गया है।

समाधान सी ++ 11 है, हालांकि यह आसानी से अगर एक भी copy_n() लिखते सी ++ 98 में बदला जा सकता है।

#include <vector> 
#include <string> 
#include <sstream> 
#include <iterator> 
#include <iostream> 
#include <stdexcept> 
#include <algorithm> 

template < 
    typename Container, 
    typename OutIter, 
    typename ChunkSepFunctor 
> 
OutIter chunker(
    Container const& c, 
    typename Container::size_type const& k, 
    OutIter o, 
    ChunkSepFunctor sep 
) 
{ 
    using namespace std; 

    if (k <= 0) 
    throw domain_error("chunker() requires k > 0"); 

    auto chunkBeg = begin(c); 
    for (auto left=c.size(); left != 0;) 
    { 
    auto const skip = min(left,k); 

    o = copy_n(chunkBeg, skip, o); 

    left -= skip; 
    advance(chunkBeg, skip); 

    if (left != 0) 
     sep(); 
    } 
    return o; 
} 

int main() 
{ 
    using namespace std; 

    using VECTOR = vector<string>; 
    VECTOR v{"a","b","c","d","e"}; 

    for (VECTOR::size_type k = 1; k < 7; ++k) 
    { 
    cout << "k = " << k << "..." << endl; 
    chunker(
     v, k, 
     ostream_iterator<VECTOR::value_type>(cout), 
     []() { cout << endl; } 
    ); 
    } 
    cout << endl; 
} 

संपादित करें: बेहतर होगा ताकि sep functor उत्पादन iterator प्राप्त की और एक आउटपुट इटरेटर लौटे chunker() लिखने के लिए होगा। इस तरह आउटपुट इटरेटर से संबंधित हिस्सों को आउटपुट करने के बीच कोई भी अपडेट सही ढंग से संभाला जा सकता है और जेनेरिक दिनचर्या कहीं अधिक लचीला हो सकता है। (उदाहरण के लिए, यह मज़दूर को प्रत्येक खंड की अंतिम स्थिति याद रखने की अनुमति देगा; फंक्चर को टुकड़ों की प्रतिलिपि बनाने, कंटेनर को खाली करने, और आउटपुट इटरेटर को रीसेट करने के लिए; आदि) यदि यह अवांछित है, तो मानक लाइब्रेरी की तरह कोई भी अलग-अलग sep आवश्यकताओं के साथ एक से अधिक अधिभार हैं, या पूरी तरह से तर्क को समाप्त कर रहे हैं। यह अद्यतन chunker() इस तरह दिखता है:

template < 
    typename Container, 
    typename OutIter, 
    typename ChunkSepFunctor 
> 
OutIter chunker(
    Container const& c, 
    typename Container::size_type const& k, 
    OutIter o, 
    ChunkSepFunctor sep 
) 
{ 
    using namespace std; 

    if (k <= 0) 
    throw domain_error("chunker() requires k > 0"); 

    auto chunkBeg = begin(c); 
    for (auto left=c.size(); left != 0;) 
    { 
    auto const skip = min(left,k); 

    o = copy_n(chunkBeg, skip, o); 

    advance(chunkBeg, skip); 
    left -= skip; 

    if (left != 0) 
     o = sep(o); 
    } 
    return o; 
} 

और हिस्सा करने के लिए कॉल किया जाएगा कम सुंदर लेकिन आम तौर पर और अधिक उपयोगी है (हालांकि नहीं इस मामले में):

chunker(
    v, k, 
    ostream_iterator<VECTOR::value_type>(cout), 
    [](ostream_iterator<VECTOR::value_type> o) { cout << endl; return o; } 
); 

यह निकला है एक होने के लिए आश्चर्यजनक रूप से सुरुचिपूर्ण छोटी दिनचर्या।

+0

धन्यवाद पॉल। 'Std :: copy_n()' और 'std :: अग्रिम()' का उपयोग करना एक और सरल और सुरुचिपूर्ण विकल्प है। मुझे 'चंकर' हस्ताक्षर और पूरे एल्गोरिदम सामान्यता पसंद है। – bartek

0

मैं थोड़ा @BenjaminB द्वारा anwser संशोधित करते हैं और इस समारोह का उपयोग करने का एक उदाहरण कहा:

#include <iostream> 
#include <vector> 

using namespace std; 

template<typename Iterator, typename Func> 
void chunks(Iterator begin, 
      Iterator end, 
      iterator_traits<string::iterator>::difference_type k, 
      Func f) 
{ 
    Iterator chunk_begin; 
    Iterator chunk_end; 
    chunk_end = chunk_begin = begin; 

    do 
    { 
     if(std::distance(chunk_end, end) < k) 
      chunk_end = end; 
     else 
      std::advance(chunk_end, k); 
     f(chunk_begin,chunk_end); 
     chunk_begin = chunk_end; 
    } 
    while(std::distance(chunk_begin,end) > 0); 
} 

int main() { 
    string in_str{"123123123"}; 

    vector<string> output_chunks; 

    auto f = [&](string::iterator &b, string::iterator &e) 
    { 
     output_chunks.emplace_back(b, e); 
    }; 

    chunks(in_str.begin(), in_str.end(), 3, f); 

    for (string a_chunk: output_chunks) 
    { 
     cout << a_chunk << endl; 
    } 

    return 0; 
} 

परिणाम है:

123 
123 
123 

आशा किसी को यह उपयोगी मिल जाएगा।

संबंधित मुद्दे