简体   繁体   English

std :: for_each在多个迭代器范围内工作

[英]std::for_each working on more than one range of iterators

The lambda notation has made stl algorithms more accessible. lambda表示法使得stl算法更易于访问。 I am still learning to decide when it's useful and when to fall back to good old fashioned for-loops. 我仍然在学习什么时候它有用以及何时回归到老式的for-loops。 Often, it becomes necessary to iterate over two (or more) containers of the same size, such that corresponding elements are related, but for some reason are not packed into the same class. 通常,有必要迭代两个(或更多)相同大小的容器,使得相应的元素是相关的,但由于某种原因不会被打包到同一个类中。

A function using a for-loop to achieve that would look like this: 使用for循环实现的函数如下所示:

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

In order to use for_each, I need a version of it that handles multiple ranges; 为了使用for_each,我需要一个处理多个范围的版本; something like: 就像是:

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

With this version, the above code would look like this: 使用此版本,上面的代码如下所示:

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

Is there an equivalent way of achieving the same result using stl algorithms? 是否有使用stl算法实现相同结果的等效方法?

EDIT 编辑

I found the exact answer to my question in the form of boost::for_each running on boost::range. 我在boost :: range上以boost :: for_each的形式找到了我的问题的确切答案。 I added the answer, with example code for the sake of completeness. 为了完整起见,我添加了答案,并附带示例代码。

1) The algorithms in the STL are not meant to cover every possible case, if you need for_each_on_two_ranges then write it (as you have) and use it. 1)STL中的算法并不意味着涵盖所有可能的情况,如果你需要for_each_on_two_ranges然后写它(就像你有)并使用它。 The beauty of the STL is it's so extensible, and you've extended it with a useful new algo. STL的美妙之处在于它是如此可扩展,并且你用一个有用的新算法扩展它。

2) If that doesn't work, you don't have to use good old fashioned for-loops, you can use fancy new for-loops instead! 2)如果这不起作用,你不必使用旧的老式for循环,你可以使用花哨的新for循环!

As another answer said, boost::zip_iterator is your friend here, but it doesn't have to be hard to use. 正如另一个答案所说, boost::zip_iterator在这里是你的朋友,但它不一定boost::zip_iterator Here's a solution using a range adaptor that is implemented with zip_iterator 这是使用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();
}

That zip function creates an adaptor with begin() and end() members that return a boost::zip_iterator , so the loop variable is a tuple of the elements of each underlying container (and as it's a variadic template you can do it for any number of containers, so you don't need to write for_each_for_three_ranges and for_each_for_four_ranges etc.) 这个zip函数创建了一个带有begin()end()成员的适配器,它们返回一个boost::zip_iterator ,所以循环变量是每个底层容器元素的元组(因为它是一个可变参数模板,你可以为任何容器数量,因此您不需要编写for_each_for_three_rangesfor_each_for_four_ranges等。)

You could also use it with for_each 您也可以将它与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();
    }
});

After reading up on boost::zip_iterator and boost::iterator_range as suggested by some answers, I came across the extension algorithms in boost::range , and found the exact parallel of the algorithm I wrote for two ranges, but with boost ranges. 在阅读了一些答案所建议的boost :: zip_iterator和boost :: iterator_range之后,我遇到了boost :: range中扩展算法 ,并找到了我为两个范围编写的算法的完全并行,但是有了增强范围。

A working code for the example would be 该示例的工作代码将是

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

Some wrappers and utility functions, similar in mind to what @Jonathan Wakely suggested, can make this even more usable. 一些包装器和实用程序功能,与@Jonathan Wakely建议的相似,可以使它更加实用。

std::transform has an overload that operates on two sequences in parallel. std::transform有一个并行操作两个序列的重载。 You'd need a null output iterator to absorb the results though, if you weren't interested in collecting any. 如果你不想收集任何结果,你需要一个空的输出迭代器来吸收结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM