2012-11-09 52 views
11

Tôi đang cố gắng để chuỗi một boost::adaptors::transformed (hãy gọi nó là map) đến boost::adaptors::filtered (hãy gọi nó là filter) - ý tưởng là ánh xạ một fun trả về "Có thể" (trong trường hợp của tôi là std::pair<bool, T>) trên một phạm vi và chỉ xuất ra một phần kết quả. thực hiện đầu tiên của tôi:boost :: adapter :: chuyển đổi theo sau tăng :: adapter :: lọc cuộc gọi chức năng hai lần

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

Khi tôi chạy này, tôi nhận được:

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 

Mỗi lần predicatetrue, fun được gọi là hai lần. Đây có phải là hành vi mong đợi không? Tôi đang làm điều gì đó sai, hoặc là/là một lỗi trong Tăng cường (Tôi đang sử dụng 1,48)?

Chỉnh sửa: Tôi đã thử điều này trên phiên bản thân của Boost và vẫn xảy ra.

Trả lời

10

Lần đầu tiên nó được gọi khi được chuyển đến bộ lọc của bạn - trong khi tăng.

Lần thứ hai nó được gọi trong phạm vi dựa trên của bạn - trong khi tham khảo. Nó không kết quả cache.

Tức là, chỉ đi qua khoảng:

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

gives:

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

Kiểm tra việc thực hiện các filter_iterator (lọc dựa vào nó). Nó không làm bất kỳ bộ nhớ đệm nào.

Điều gì sẽ xảy ra nếu chuyển đổi đắt tiền?

được lọc không sử dụng knowladge nơi đầu vào của nó đến.

Lưu vào bộ nhớ kết quả sẽ yêu cầu kích thước của các trình vòng lặp được lọc. Chỉ cần nghĩ rằng nơi lưu trữ kết quả được lưu trữ. Nó sẽ được sao chép vào một số thành viên của iterator lọc.

Vì vậy, về cơ bản, có sự cân bằng giữa không gian để lưu vào bộ nhớ đệm và số lượng cuộc hội thảo.


EDIT: Tôi đã thực hiện bằng chứng về cached_iterator lưu trữ kết quả của việc dereference, và vô hiệu hóa nó trên mỗi tiến. Ngoài ra, tôi đã thực hiện bộ chuyển đổi phạm vi tương ứng.

đây như thế nào nó được sử dụng:

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

Bạn nên đặt cache trong chuỗi mà bạn muốn để cache kết quả.

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

Output là:

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

Vâng, tôi chỉ figured mà ra sau khi xem xét thông qua các mã. Điều đó có ý nghĩa đối với 'chuyển đổi', gọi' fun' hai lần không? Suy nghĩ về các ngôn ngữ khác (Python, Haskell, v.v., nó không có ý nghĩa). Điều gì sẽ xảy ra nếu việc chuyển đổi tốn kém? –

+1

Trong trường hợp đó, bạn có thể sử dụng bộ điều hợp "được lưu trong bộ nhớ cache" như được hiển thị trong câu trả lời. –

+1

Cảm ơn bộ điều hợp được lưu trong bộ nhớ cache! Nó giải quyết vấn đề :) –

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