【问题标题】:std::for_each working on more than one range of iteratorsstd::for_each 处理多个迭代器
【发布时间】:2012-04-16 02:02:02
【问题描述】:

lambda 表示法使 stl 算法更易于使用。我仍在学习决定什么时候有用,什么时候回退到老式的 for 循环。 通常,有必要迭代两个(或更多)相同大小的容器,以便相应的元素相关,但由于某种原因没有打包到同一个类中。

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

为了使用for_each,我需要一个处理多个范围的版本;类似:

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

使用这个版本,上面的代码看起来像这样:

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

是否有使用 stl 算法实现相同结果的等效方法?

编辑

我以在 boost::range 上运行的 boost::for_each 的形式找到了我的问题的确切答案。为了完整起见,我添加了答案,并附有示例代码。

【问题讨论】:

  • 为什么不直接使用你已经写好的for_each_two_ranges
  • 这对我来说似乎很常见,我认为它已经被某人解决了
  • 我认为来自 Boost.Iterator 的 zip_iterator 可以满足您的需求。详情请见boost.org/doc/libs/1_49_0/libs/iterator/doc/zip_iterator.html
  • 谢谢celtschk,我相信你是对的。但是使用 zip_iterator 的开销让我倾向于更简单的方案
  • 只要迭代器本身是独立的,您就无法合理地将语义抽象到函数中。看起来序列在两个可迭代结构之间具有足够的隐含共性,您可以对某种映射做出假设,但您必须牺牲您所做的事情的通用吸引力以获得一些句法简单性。我想说的是,除非你真的经常这样做,否则你已经提出了一个绰绰有余的解决方案。

标签: c++ boost stl c++11


【解决方案1】:

1) STL 中的算法并不意味着涵盖所有可能的情况,如果您需要for_each_on_two_ranges,请编写它(如您所愿)并使用它。 STL 的美妙之处在于它的可扩展性非常好,而且您已经使用有用的新算法对其进行了扩展。

2) 如果这不起作用,您不必使用老式的 for 循环,而是可以使用花哨的新 for 循环!

正如另一个答案所说,boost::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();
}

zip 函数创建了一个带有 begin()end() 成员的适配器,这些成员返回一个 boost::zip_iterator,因此循环变量是每个底层容器的元素的元组(并且因为它是一个可变参数模板,您可以对任意数量的容器执行此操作,因此您无需编写 for_each_for_three_rangesfor_each_for_four_ranges 等)

您也可以将其与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();
    }
});

【讨论】:

  • 看起来很不错。我喜欢它可变参数的事实,并且代码相对紧凑。我看到我需要在范围适配器上做一些功课。谢谢!
  • redi 是 boost 中的命名空间吗?什么是必需的标题?谢谢。
  • @JiveDadson,不,这是我自己的命名空间,我将工作 zip 链接到源代码(现在又做了一次)
  • @Jonathan 请原谅我太密集了。我认为您编写了链接到的代码。对吗?
  • @JiveDadson,是的,如代码顶部的版权注释中所述。
【解决方案2】:

在阅读了一些答案所建议的 boost::zip_iterator 和 boost::iterator_range 之后,我遇到了extension algorithms in boost::range,并发现我为两个范围编写的算法完全平行,但具有提升范围。

该示例的工作代码是

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

一些包装器和实用功能,类似于@Jonathan Wakely 所建议的,可以使它更加有用。

【讨论】:

  • 即使在 Windoze 上,包含路径也应始终使用 / 而不是 \,否则 "path\names\like\this.hpp" 将包含换行符和 TAB 字符。此外,它使它们可移植到所有系统,而不仅仅是 Windoze。
  • sn-p 现在对 Unix 友好,@JonathanWakely :)
  • 请注意,您可以只写boost::for_each(data, prop, ...) - 无需调用make_iterator_range
【解决方案3】:

std::transform 有一个在两个序列上并行运行的重载。但是,如果您对收集结果不感兴趣,则需要一个空输出迭代器来吸收结果。

【讨论】:

  • for_each 和 transform 有不同的语义 link。该标准要求转换是非变异的,但不要求 for_each 的转换。无论如何,您提到的重载仅适用于相同的前向迭代器
  • @Killogre: std::transform 允许通过将输出迭代器设置为输入迭代器之一来进行变异。但是,是的,它并不是真正针对这种情况而设计的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
相关资源
最近更新 更多