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

for_each that gives two (or n) adjacent elements

本文关键字:元素 两个 each for      更新时间:2023-10-16

是否有一个标准实现的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 范围的 for 或 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>

临时解决方案

我已经在CR上发布了我自己的解决方案。我不会在这里重复它,因为这是一个标准解决方案,并且已经有足够的自己滚动答案。

您实际上可以为此滥用std::uniquestd::adjacent_find:谓词与迭代器范围内的每一对连续对一起调用,只要谓词始终返回 false,它就不会修改任何内容或提前返回。

忽略这个特定的黑客,我会将其实现为迭代器适配器,而不是算法。也就是说,我将实现一个consecutive_tuple_iterator<N>它将返回 N 个连续项目的所有元组。然后你可以用它来做count_ifincludes之类的事情,而不仅仅是for_each。(不过,它不适合大多数修改算法。

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

 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. 它是按值传递的。

func 可能会"积累"某种状态,但我们在此算法中所做的副本正在积累它,而不是用户的原始对象。因此,我们将修改后的"func"对象传回它们。

对于 C++ 11 和迭代器的新迭代器辅助函数 std::next 和 std::p rev,标准算法 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
        >
    );
}

不完全是你想要的,但看看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);

留意C++23 浏览::slide(n)

vector<int> ints = {1, 2, 3, 4};
auto rg2 = ints | views::slide(2);
/*
{{1, 2}, {2, 3}, {3, 4}}
*/
auto rg3 = ints | views::slide(3);
/*
{{1, 2, 3},{2, 3, 4}}
*/
相关文章: