2012-04-02 61 views
11

lambda表示法使得stl算法更易于访问。我仍然在学习如何决定什么时候有用,什么时候回到老式的循环。 通常,就有必要迭代相同大小的两个(或多个)容器中,以使得相应的元件是相关的,但由于某些原因未打包到同一类别。std :: for_each处理多个迭代器范围

一个函数中使用一个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 ::升压::范围运行的for_each的形式。为了完整起见,我添加了答案和示例代码。

+1

为什么不直接使用已经写好的'for_each_two_ranges'? – Puppy 2012-04-02 11:58:28

+1

它看起来像我这么普遍,我认为它已经被某人已经解决了 – killogre 2012-04-02 12:01:25

+6

我认为来自Boost.Iterator的'zip_iterator'做你想做的。有关详细信息,请参阅http://www.boost.org/doc/libs/1_49_0/libs/iterator/doc/zip_iterator.html。 – celtschk 2012-04-02 12:23:51

回答

8

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

使用10
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

这看起来很不错。我喜欢它可变的事实,并且代码相对紧凑。我发现我需要在范围适配器上做些功课。谢谢! – killogre 2012-05-05 17:30:55

+0

redi是一个命名空间的提升?什么是必需的标题? Thankee。 – 2012-08-22 21:27:11

+0

@JiveDadson,不,这是我自己的命名空间,我将工作[zip](https://gitorious.org/redistd/redistd/blobs/master/include/redi/zip.h)链接到源代码(并再次执行现在) – 2012-08-22 21:34:16

1

std::transform具有并行地对两个序列进行操作的过载。如果你不想收集任何结果,你需要一个空输出迭代器来吸收结果。

+0

for_each和transform有不同的语义[链接](http://drdobbs.com/cpp/184403769)。该标准要求变换是非变异的,但不要求来自for_each。无论如何,你提到的重载只适用于相同的迭代器 – killogre 2012-04-02 17:28:53

+0

@Killogre:'std :: transform'允许通过将输出迭代器设置为其中一个输入迭代器来允许变化。但是,这不是真的为这种确切的情况设计的。 – 2012-04-02 18:56:28

5

上的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建议类似,可以让这个更可用。

+0

包含路径应​​该总是使用/不\ \即使在Windoze上,否则路径\名称\像\ this.hpp“'将有新行和制表符字符。再加上它使它们可以移植到所有系统,而不仅仅是Windoze。 – 2012-08-22 22:44:26

+0

这段代码现在是Unix友好的,@JonathanWakely :) – killogre 2012-08-30 06:20:22

+2

请注意,您可以只写'boost :: for_each(data,prop,...)' - 不需要调用'make_iterator_range'。 – ecatmur 2014-02-13 09:28:09