c++收集算法

C++ collect algorithm?

本文关键字:算法 c++      更新时间:2023-10-16

每隔一段时间,我需要迭代容器元素的一个子集,或者只是想提取它们并忽略其余部分。我最终使用boost::range::adaptors::filtered来创建这个惰性集合。

for(auto&& i : container | filtered(predicate)) {
  // .. do stuff
}

是否有理由在STL中缺乏收集算法(如Ruby的collect)(我们只有copy_if,这是不一样的)?或者有什么理由反对使用它吗?

一个可能的实现是:

template<class Container, class Predicate>
Container collect(Container&& c, Predicate&& p) {
  Container n;
  for(auto&& i : c) {
    if(p(i)) {
      n.push_back(i);
    }
  }
  return n;
}

,但lazy_collect也可能有助于避免复制。

下面所有的答案都很好。我真希望我能把它们都标记出来。我不知道std::back_inserter。现在收集东西就像这样简单:

boost::copy( orig | filtered(predicate), std::back_inserter(collection));

标准算法不能直接操作容器(创建或销毁容器,或改变容器的大小)。它们对迭代器范围进行操作,算法所做的唯一改变是通过迭代器赋值。

所以,你提议的操作完全超出了标准算法的范围。也许标准中"应该"有大量额外的泛型容器操作集,包括这个,但是"STL哲学"是在迭代器上操作。

您建议的非延迟操作可以使用std::back_inserterstd::copy_if(可选择使用移动迭代器)或move_if(如果您自己滚动)完成。C++03中缺少std::copy_if,但这是一个偶然的疏忽。

惰性版本不能仅仅通过将标准组件连接在一起来完成——没有一种干净的方法可以在没有中间存储的情况下将一种算法的"输出"直接链接到另一种算法的"输入"。这就是为什么Boost提供了如此有趣的各种迭代器,以及range。

我不知道为什么它们没有被合并到c++ 11中,但是关于c++ 11有一点需要注意,那就是它太迟了。提案因为缺乏时间而被放弃,所以原因可能很简单,"考虑到已知的现有工作量,它从未被认为重要到足以提出建议"。

考虑到您的特定实现,并非所有容器都有push_back,因此您可能不应该使用Container作为模板参数名。PushBackSequence将更能描述需求。

这个怎么样:

#include <algorithm>
#include <iterator>
std::copy_if(container.begin(), container.end(), std::back_inserter(result), []{...})

其中container为要收集的容器,result为存储收集的容器

从注释中:

当使用copy_if时,你需要事先知道你需要多少空间

那不是真的。您可以使用std::copy_if与反向插入器:

#include <algorithm> // For std::copy_if
#include <iterator> // For std::back_inserter
#include <vector>
#include <iostream>
int main()
{
    std::vector<int> v(10);
    std::iota(begin(v), end(v), 0); // Fills the vector with 0..9
    std::vector<int> out;
    std::copy_if(begin(v), end(v), back_inserter(out), [] (int x) 
    //                             ^^^^^^^^^^^^^^^^^^
    {
        return x > 4; 
    });
    for (auto x : out) { std::cout << x << " "; }
}

下面是一个实例。

如果你想要一个直接作用于容器的函数,而不是作用于一对迭代器定义的范围,你可以写下面的帮助:

template<typename C1, typename C2, typename P>
void cont_copy_if(C1&& src, C2&& dst, P&& p)
{
    std::copy_if(
        begin(std::forward<C1>(src)),
        end(std::forward<C1>(src)),
        back_inserter(std::forward<C2>(dst)),
        std::forward<P>(p)
        );
}

你可以这样使用:

int main()
{
    std::vector<int> v(10);
    std::iota(begin(v), end(v), 0);
    std::list<int> out;
//  ^^^^^^^^^
//  You could use a different destination container
    cont_copy_if(v, out, [] (int x) { return x > 4; });
//  ^^^^^^^^^^^^
    for (auto x : out) { std::cout << x << " "; }
}

当然还有实际的例子