2012-04-02 46 views
11

Ký hiệu lambda đã làm cho các thuật toán stl dễ tiếp cận hơn. Tôi vẫn đang học cách quyết định khi nào nó hữu ích và khi nào thì quay trở lại với các vòng lặp cũ. Thông thường, nó trở nên cần thiết để lặp qua hai (hoặc nhiều hơn) các thùng chứa có cùng kích thước, sao cho các phần tử tương ứng có liên quan, nhưng vì một lý do nào đó không được đóng gói trong cùng một lớp.std :: for_each làm việc trên nhiều loại vòng lặp

Một chức năng sử dụng một cho vòng lặp để đạt được điều đó sẽ trông như thế này:

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    auto i_data = begin(data); 
    auto i_prop = begin(prop); 
    for (; i_data != data.end(); ++i_data, ++i_prop) { 
     if (i_prop->SomePropertySatistfied()) { 
      i_data->DoSomething(); 
     } 
    } 
} 

Để sử dụng for_each, tôi cần một phiên bản của nó để xử lý nhiều dãy; cái gì đó như:

template<typename InputIter1, typename InputIter2, typename Function> 
Function for_each_on_two_ranges(InputIter1 first1, InputIter1 last1, InputIter2 first2, Function f) { 
    for (; first1 != last1; ++first1, ++first2) { 
     f(*first1, *first2); 
    } 
    return f; 
} 

Với phiên bản này, các mã trên sẽ trông như thế này:

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    for_each_on_two_ranges(begin(data), end(data), begin(prop), [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

Có một cách tương đương để đạt được kết quả tương tự sử dụng các thuật toán STL?

EDIT

Tôi tìm thấy câu trả lời chính xác cho câu hỏi của tôi dưới hình thức tăng :: for_each chạy trên boost :: tầm xa. Tôi đã thêm câu trả lời, với mã ví dụ vì mục đích hoàn chỉnh.

+1

Tại sao không chỉ sử dụng 'for_each_two_ranges' mà bạn đã viết? – Puppy

+1

Nó trông giống như một cái gì đó rất phổ biến với tôi, rằng tôi nghĩ rằng nó đã được giải quyết bởi ai đó đã – killogre

+6

Tôi nghĩ 'zip_iterator' từ Boost.Iterator làm những gì bạn muốn. Xem http://www.boost.org/doc/libs/1_49_0/libs/iterator/doc/zip_iterator.html để biết chi tiết. – celtschk

Trả lời

8

1) Các thuật toán trong STL không có nghĩa là bao gồm mọi trường hợp có thể, nếu bạn cần for_each_on_two_ranges thì hãy viết nó (như bạn có) và sử dụng nó. Vẻ đẹp của STL là nó rất mở rộng, và bạn đã mở rộng nó với một bản ngã mới hữu ích.

2) Nếu điều đó không hiệu quả, bạn không phải sử dụng các vòng lặp thời trang cũ, bạn có thể sử dụng các vòng lặp mới lạ mắt thay thế!

Như một câu trả lời khác cho biết, boost::zip_iterator là bạn của bạn ở đây nhưng không khó sử dụng. Dưới đây là một giải pháp sử dụng một bộ chuyển đổi phạm vi đó được thực hiện với zip_iterator

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    for (auto i : redi::zip(data, prop)) 
     if (i.get<1>().SomePropertySatistfied()) 
      i.get<0>.DoSomething(); 
} 

Đó zip chức năng tạo ra một bộ chuyển đổi với begin()end() thành viên trả về một boost::zip_iterator, vì vậy biến vòng lặp là một tuple trong những yếu tố của mỗi thùng chứa tiềm ẩn (và vì nó là một mẫu variadic bạn có thể làm điều đó cho bất kỳ số lượng container, do đó bạn không cần phải viết for_each_for_three_rangesfor_each_for_four_ranges vv)

bạn cũng có thể sử dụng nó với for_each

auto z = redi::zip(data, prop); 
typedef decltype(z)::iterator::reference reference; 

for_each(begin(z), end(z), [](reference i) { 
    if (i.get<1>().SomePropertySatistfied()) { 
     i.get<0>().DoSomething(); 
    } 
}); 
+0

Điều đó trông rất đẹp. Tôi thích thực tế rằng nó variadic, và rằng mã tương đối nhỏ gọn. Tôi thấy rằng tôi cần phải làm một số bài tập về nhà trên bộ điều hợp phạm vi. Cảm ơn! – killogre

+0

Là redi một không gian tên trong tăng? Tiêu đề bắt buộc là gì? Cám ơn. –

+0

@ JiveDadson, không có không gian tên riêng của tôi, tôi đã liên kết công việc [zip] (https://gitorious.org/redistd/redistd/blobs/master/include/redi/zip.h) với nguồn (và đã làm lại) bây giờ) –

1

std::transform có tình trạng quá tải hoạt động trên hai trình tự song song. Bạn sẽ cần một vòng lặp đầu ra null để hấp thụ các kết quả mặc dù, nếu bạn không quan tâm đến việc thu thập bất kỳ.

+0

for_each và biến đổi có ngữ nghĩa khác nhau [link] (http://drdobbs.com/cpp/184403769). tiêu chuẩn yêu cầu biến đổi là không biến đổi, nhưng không đòi hỏi điều đó từ for_each. Dù sao, quá tải bạn đề cập chỉ hoạt động cho các trình vòng lặp chuyển tiếp giống hệt nhau – killogre

+0

@Killogre: 'std :: transform' cho phép đột biến bằng cách đặt trình vòng lặp đầu ra thành một trong các trình vòng lặp đầu vào. Nhưng có, nó không thực sự được thiết kế cho tình huống chính xác này. –

5

Sau khi đọc hết boost :: zip_iterator và boost :: iterator_range theo gợi ý của một số câu trả lời, tôi đã xem qua số extension algorithms in boost::range và tìm song song chính xác của thuật toán tôi đã viết cho hai phạm vi.

Mã làm việc ví dụ như sẽ

#include <boost/range/algorithm_ext/for_each.hpp> 

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    auto rng1 = boost::make_iterator_range(data.begin(), data.end()); 
    auto rng2 = boost::make_iterator_range(prop.begin(), prop.end()); 
    boost::for_each(rng1, rng2, [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

Một số giấy gói và chức năng tiện ích, tương tự như trong tâm trí những gì @ Jonathan Wakely đề nghị, có thể làm cho điều này thậm chí nhiều hơn có thể sử dụng.

+0

Bao gồm đường dẫn luôn luôn nên sử dụng/không \ ngay cả trên Windoze, nếu không '" path \ names \ like \ this.hpp "' sẽ có newline và TAB ký tự in. Plus nó làm cho chúng di động cho tất cả các hệ thống, không chỉ Windoze. –

+0

Đoạn mã này giờ đây thân thiện với Unix, @JonathanWakely :) – killogre

+2

Lưu ý rằng bạn chỉ có thể viết 'boost :: for_each (dữ liệu, chống đỡ, ...)' - không cần phải gọi 'make_iterator_range'. – ecatmur

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