【问题标题】:Find all matching elements in std::list查找 std::list 中的所有匹配元素
【发布时间】:2014-12-13 02:14:33
【问题描述】:

我想知道是否有任何内置或成熟的方法(即通过 lambda)来遍历 std::list 的元素并找到所有与给定值匹配的元素?我知道我可以遍历所有这些,但我想我会问是否有办法获得一个迭代器,它只遍历与给定条件匹配的元素?下面的示例只为我提供了第一个匹配元素的迭代器。

#include <list>
#include <algorithm>
#include <stdio.h>

int main()
{
    std::list<int> List;
    List.push_back(100);
    List.push_back(200);
    List.push_back(300);
    List.push_back(100);
    int findValue = 100;

    auto it = std::find_if(List.begin(), List.end(), [findValue](const int value)
    {
        return (value == findValue);
    });

    if (it != List.end())
    {
        for (; it != List.end(); ++it)
        {
            printf("%d\n", * it);
        }
    }
    return 0;
}

感谢您的任何反馈。

【问题讨论】:

  • 重复调用find_if 以查找剩余的匹配项,只需在每次调用之前递增开始迭代器(假设最后一次调用导致匹配)。如果您经常这样做,请编写一个函数。如果您所做的只是在谓词中使用operator==,则不需要find_iffind 会为您做到这一点。
  • “如果有办法获得一个迭代器,它只遍历与给定条件匹配的元素” 也许Filter Iterators?

标签: c++ c++11 stdlist


【解决方案1】:

更新答案

随着 C++20 的到来,标准库现在引入了 ranges 的概念,它与视图适配器一起提供,只是对集合及其转换的惰性视图。

这意味着您现在可以拥有一个“迭代器”,它可用于获取底层容器/集合的过滤和转换视图,而无需创建多个迭代器甚至分配内存。

话虽如此,这是一种仅在列表中过滤的元素上创建视图的方法:

// List is your std::list
auto matching_100 = List | std::views::filter([](auto &v) {
  return v == 100;
});

这有多甜?您需要使用所有这些吗?

#include <ranges>

Try it out


上一个答案

使用copy_ifiterators

#include <list>
#include <algorithm>
#include <iterator>
#include <iostream>

int main()
{
    std::list<int> List;
    List.push_back(100);
    List.push_back(200);
    List.push_back(300);
    List.push_back(100);
    int findValue = 100;

    std::copy_if(List.begin(), List.end(), std::ostream_iterator<int>(std::cout, "\n"), [&](int v) {
        return v == findValue;
    });
    return 0;
}

如果您不想直接输出结果并想用匹配项填充另一个容器:

std::vector<int> matches;
std::copy_if(List.begin(), List.end(), std::back_inserter(matches), [&](int v) {
    return v == findValue;
});

【讨论】:

    【解决方案2】:

    boost::filter_iterator 允许您仅使用满足谓词的迭代元素。给定一个谓词Pred 和一个容器Cont

    auto begin_iter = boost::make_filter_iterator(Pred, std::begin(Cont), std::end(Cont));
    auto end_iter = boost::make_filter_iterator(Pred, std::end(Cont), std::end(Cont));
    

    您现在可以使用begin_iterend_iter,就好像它们是只包含满足PredCont 元素的容器的开始和结束迭代器。另一个额外的优势是您可以将迭代器包装在 boost::iterator_range 中,并在需要可迭代对象的地方使用它,例如基于范围的 for 循环,如下所示:

    auto range = boost::make_iterator_range(begin_iter, end_iter);
    for(auto x : range) do_something(x);
    

    特别是,将Pred 设置为检查与固定值是否相等的函子(可能是 lambda)将为您提供所需的迭代器。

    【讨论】:

    • #include #include
    • 问题是关于 std::list;标准库中包含的答案是合法的。
    【解决方案3】:

    std::find_ifstd::find 的概括,当您需要一个函数来检查您想要的元素时,而不是简单的相等性测试。如果您只想对相等性进行简单测试,则不需要广义形式,而 lambda 只会增加复杂性和冗长性。只需改用std::find(begin, end, findValue)

    std::vector<std::list<int>::const_iterator> matches;
    auto i = list.begin(), end = list.end();
    while (i != end)
    {
      i = std::find(i, end, findValue);
      if (i != end)
        matches.push_back(i++);
    }
    

    但我不会在循环中调用find,而是手动编写循环:

    std::vector<std::list<int>::const_iterator> matches;
    for (auto i = list.begin(), toofar = list.end(); i != toofar; ++i)
      if (*i == findValue)
        matches.push_back(i);
    

    【讨论】:

    • 不会std::copy() 只检查整个列表一次 O(N),而你的会检查 O(x*N)?最好也看看其他答案:) @smac89 有一个很好的波纹管
    • @Paiusco 为什么我的不是 O(N)?每个 std::find 从上一个查找停止的地方开始。
    【解决方案4】:

    std::partition 让您可以简单地将与谓词匹配的所有元素移动到容器的前面(第一个分区)。返回值是一个迭代器,指向第二个分区的第一个元素(包含不匹配的元素)。这几乎就是您“过滤”容器所需的全部内容。

    【讨论】:

      猜你喜欢
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 2013-12-06
      • 1970-01-01
      • 2022-01-11
      • 2013-03-18
      • 1970-01-01
      相关资源
      最近更新 更多