2012-11-09 15 views
11

मैं श्रृंखला के लिए कोशिश कर रहा हूँ एक boost::adaptors::transformed एक boost::adaptors::filtered करने के लिए (यह filter कॉल) (यह map कॉल) - विचार करने के लिए है एक fun मानचित्र करें जो एक "हो सकता है" (मेरे मामले में, std::pair<bool, T>) एक सीमा पर और परिणामों का केवल एक हिस्सा आउटपुट देता है। मेरी पहली कार्यान्वयन:बढ़ावा :: एडेप्टर :: बढ़ावा :: एडाप्टर के बाद बदल :: फ़िल्टर्ड कॉल से कार्य दो बार

define BOOST_RESULT_OF_USE_DECLTYPE // enable lambda arguments for Boost.Range 
#include <boost/range/adaptor/filtered.hpp> 
#include <boost/range/adaptor/transformed.hpp> 

struct OnlyEven 
{ 
    typedef int argument_type; 
    typedef std::pair<bool, int> result_type; 
    result_type operator()(argument_type x) const 
    { 
     std::cout << "fun: " << x << std::endl; 
     return std::make_pair(x % 2 == 0, x); 
    } 
} only_even; 

int main(int argc, char* argv[]) 
{ 
    auto map = boost::adaptors::transformed; 
    auto filter = boost::adaptors::filtered; 
    int v[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    auto s = v | map(only_even) | filter([](std::pair<bool, int> x)->bool{ return x.first; }); 
    for (auto i : s) {} 
    return 0; 
} 

जब मैं इस चलाने के लिए, मैं:

fun: 1 
fun: 2 
fun: 2 
fun: 3 
fun: 4 
fun: 4 
fun: 5 
fun: 6 
fun: 6 
fun: 7 
fun: 8 
fun: 8 
fun: 9 
fun: 10 
fun: 10 

हर बार predicatetrue है, fun दो बार कहा जाता है। क्या यह अपेक्षित व्यवहार है? क्या मैं कुछ गलत कर रहा हूं, या बूस्ट में यह एक बग है (मैं 1.48 का उपयोग कर रहा हूं)?

संपादित करें: मैंने बूस्ट के ट्रंक संस्करण पर यह कोशिश की और यह अभी भी होता है।

उत्तर

10

पहली बार इसे आपके फ़िल्टर में पारित होने पर कहा जाता है - वृद्धि के दौरान।

दूसरी बार इसे आपकी श्रेणी-आधारित-के दौरान - dereference के दौरान बुलाया जाता है। यह परिणाम कैश नहीं करता है।

अर्थात, और सिर्फ सीमा के माध्यम से गुजर रहा:

++++++++++boost::begin(s); 

gives: filter_iterator की

fun: 1 
fun: 2 
fun: 3 
fun: 4 
fun: 5 
fun: 6 
fun: 7 
fun: 8 
fun: 9 
fun: 10 

चेक कार्यान्वयन (फ़िल्टर किए गए यह पर आधारित है)। यह कोई कैशिंग नहीं करता है।

क्या परिवर्तन महंगा है?

फ़िल्टर किए गए ज्ञान का उपयोग नहीं करते हैं जहां यह इनपुट आता है।

परिणाम के कैशिंग को फ़िल्टर किए गए इटरेटर्स के आकार को बढ़ाने की आवश्यकता होगी। बस सोचें कि कैश्ड परिणाम कहाँ संग्रहित किया जाना चाहिए। इसे फ़िल्टर किए गए इटरेटर के कुछ सदस्य में कॉपी किया जाना चाहिए।

तो, मूल रूप से, कैशिंग और डेरफ़्रेंसिंग की गिनती के लिए स्थान के बीच व्यापार-बंद है।


संपादित करें: मैं-का-प्रमाण अवधारणा cached_iterator की जो भिन्नता का परिणाम कैश, और प्रत्येक को आगे बढ़ाने पर यह अमान्य कर दिया है। इसके अलावा, मैंने इसी श्रेणी एडाप्टर बनाया है।

यह इस प्रकार से प्रयोग किया जाता है:

auto s = v | transformed(only_even) | cached | reversed | cached | flt | flt | flt | flt | flt | flt; 

आप रखूं श्रृंखला जहां परिणाम कैश करने के लिए चाहते हैं, उसमें कैश की गई।

live demo

#include <boost/range/adaptor/filtered.hpp> 
#include <boost/range/adaptor/transformed.hpp> 
#include <boost/range/adaptor/reversed.hpp> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/range/algorithm.hpp> 

#include <iostream> 
#include <ostream> 

// ____________________________________________________________________________________________ // 

#include <boost/iterator/iterator_adaptor.hpp> 
#include <boost/range/iterator.hpp> 
#include <iterator> 

namespace impl 
{ 

template<typename Iterator> 
class cached_iterator : public boost::iterator_adaptor<cached_iterator<Iterator>,Iterator> 
{ 
    typedef boost::iterator_adaptor<cached_iterator,Iterator> super; 
    mutable bool invalidated; 
    mutable typename std::iterator_traits<Iterator>::value_type cached;  
public: 
    cached_iterator() : invalidated(true) {} 
    cached_iterator(const Iterator &x) : super(x), invalidated(true) {} 

    typename std::iterator_traits<Iterator>::value_type dereference() const 
    { 
     if(invalidated) 
     { 
      cached = *(this->base()); 
      invalidated=false; 
      return cached; 
     } 
     else 
     { 
      return cached; 
     } 
    } 
    void increment() 
    { 
     invalidated=true; 
     ++(this->base_reference()); 
    } 
    void decrement() 
    { 
     invalidated=true; 
     --(this->base_reference()); 
    } 
    void advance(typename super::difference_type n) 
    { 
     invalidated=true; 
     (this->base_reference())+=n; 
    } 
}; 

template<typename Iterator> cached_iterator<Iterator> make_cached_iterator(Iterator it) 
{ 
    return cached_iterator<Iterator>(it); 
} 

template< class R > 
struct cached_range : public boost::iterator_range<cached_iterator<typename boost::range_iterator<R>::type> > 
{ 
private: 
    typedef boost::iterator_range<cached_iterator<typename boost::range_iterator<R>::type> > base; 
public: 
    typedef R source_range_type; 
    cached_range(R& r) 
     : base(make_cached_iterator(boost::begin(r)), make_cached_iterator(boost::end(r))) 
    { } 
}; 

template<typename InputRange> 
inline cached_range<const InputRange> cache(const InputRange& rng) 
{ 
    return cached_range<const InputRange>(rng); 
} 

template<typename InputRange> 
inline cached_range<InputRange> cache(InputRange& rng) 
{ 
    return cached_range<InputRange>(rng); 
} 

struct cache_forwarder{}; 

cache_forwarder cached; 

template< class InputRange > 
inline cached_range<const InputRange> 
operator|(const InputRange& r, cache_forwarder) 
{ 
    return cache(r); 
} 

template< class InputRange > 
inline cached_range<InputRange> 
operator|(InputRange& r, cache_forwarder) 
{ 
    return cache(r); 
} 

} // namespace impl 

// ____________________________________________________________________________________________ // 


struct OnlyEven 
{ 
    typedef int argument_type; 
    typedef std::pair<bool, int> result_type; 
    result_type operator()(argument_type x) const 
    { 
     std::cout << "fun: " << x << std::endl; 
     return std::make_pair(x % 2 == 0, x); 
    } 
} only_even; 

int main() 
{ 
    using namespace impl; 
    using namespace boost::adaptors; 
    using namespace std; 

    int v[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
    auto flt = filtered([](std::pair<bool, int> x)->bool{ return x.first; }); 

    auto s = v | transformed(only_even) | cached | reversed | cached | flt | flt | flt | flt | flt | flt; 

    boost::copy(s | map_values, ostream_iterator<int>(cout,"\n")); 
    return 0; 
} 

आउटपुट है:

fun: 10 
10 
fun: 9 
fun: 8 
8 
fun: 7 
fun: 6 
6 
fun: 5 
fun: 4 
4 
fun: 3 
fun: 2 
2 
fun: 1 
+0

हाँ, मैं सिर्फ इतना है कि पता लगा कोड के माध्यम से देखने के बाद। क्या यह 'रूपांतरित' के लिए समझ में आता है, जिसे 'मजेदार' दो बार बुलाया जाता है? अन्य भाषाओं के बारे में सोचना (पायथन, हास्केल, आदि यह समझ में नहीं आता है)। क्या होगा यदि परिवर्तन महंगा है? –

+1

उस स्थिति में, आप जवाब में दिखाए गए अनुसार "कैश्ड" एडाप्टर का उपयोग कर सकते हैं। –

+1

कैश एडाप्टर के लिए धन्यवाद! यह समस्या हल करता है :) –