for_each给出两个(或n个)相邻元素

时间:2013-11-12 11:10:03

标签: c++ algorithm foreach

是否有for_each的标准实现可以调用元素和范围中的下一个元素?

例如,取{0, 1, 2, 3, 4, 5}范围,我想为每个元素及其后续元素调用函数f{f(0, 1), f(1, 2), f(2, 3), f(3, 4), f(4, 5)} 注意最后一个元素是如何被遗漏的,因为它没有后继元素。

如果对n个后继者进行泛化,那么它也会很好,这些后继者会与元素本身一起传递。

到目前为止,我总是用迭代器的手写循环来解决这个问题。 但是,我想更多地基于C ++ 11范围或std::for_each来避免锅炉板代码。

实施例

// today: handwritten loop
for(Range::iterator current = range.begin(); current != range.end(); ++current) 
   f(*current, *std::next(current));

// near future: wrapped version
for_each_pair(range.begin(), range.end(), f);

// slightly further future: generalized version
for_each_tuple<n>(range.begin(), range.end(), f);

附加问题

可以改进功能的名称。对我来说for_each_pair / tuple听起来应该返回范围大小为n的所有子集(这本身就是我想要解决的另一个问题)。所以我想对更好的名字提出一些建议:

for_each_adjacent<n>

4 个答案:

答案 0 :(得分:4)

最简单的方法是将其作为通用算法编写,然后多次应用。

 template< typename FwdIter, typename Func >
 Func for_each_pair( FwdIter iterStart, FwdIter iterEnd, Func func )
 {
     if( iterStart == iterEnd )
        return func;

     FwdIter iterNext = iterStart;
     ++iterNext;

     for( ; iterNext != iterEnd; ++iterStart, ++iterNext )
     {
          func( *iterStart, *iterNext );
     }
     return func;
}

当我被问到为什么它会返回func(而不是void)时,这是for_each的典型特征,因为

  1. func可以是一个对象
  2. 按值传递。
  3. func可能会“累积”某种状态,但它是我们在此算法中所做的复制,而不是用户的原始对象。因此,我们将它们传回修改后的“func”对象。

答案 1 :(得分:3)

你可能实际上滥用std::uniquestd::adjacent_find:在迭代器范围内使用每个连续对调用谓词,并且只要谓词总是返回false,它就不会修改任何东西或早点回来。

忽略那个特定的hack,我会把它实现为迭代器适配器,而不是算法。也就是说,我实现了consecutive_tuple_iterator<N>,它将返回N个连续项的所有元组。然后,您可以将其用于count_ifincludes等内容,而不仅仅是for_each。 (但这对大多数修改算法来说都不合适。)

答案 2 :(得分:3)

使用C ++ 11和迭代器的新迭代器辅助函数std::nextstd::prev,标准算法std::transform的第二个变体可用于迭代相邻元素。

以下是从列表中生成相邻对的列表的示例:

std::vector<int> nums{3, 4, 2, 9, 15, 267};
std::vector<std::pair<int,int>> num_pairs;
if (!nums.empty()) {
    std::transform(
        std::begin(nums), std::prev(std::end(nums)),
        std::next(std::begin(nums)),
        std::back_inserter(num_pairs),
        std::make_pair<
            decltype(nums)::const_reference,
            decltype(nums)::const_reference
        >
    );
}

答案 3 :(得分:1)

不完全符合您的要求,但请查看cpplinq

int numbers[] = {0, 1, 2, 3, 4, 5};
auto pairs = cpplinq::from_array(numbers) 
          >> cpplinq::pairwise() 
          >> cpplinq::to_vector(); // yields (0,1), (1,2), (2,3), (3,4), (4,5)

for(auto p : pairs)
    f(p.first, p.second);