【问题标题】:How to filter object in c++ [duplicate]如何在c ++中过滤对象[重复]
【发布时间】:2016-09-06 21:29:41
【问题描述】:

假设我有一堂课:

class Cell {
  int ID;
  int valueX;
  int valueY;
};

在 main() 中我声明了 Cells 的向量:

vector<Cell> myCells;

我的问题是编写一个函数,它获取单元格向量、运算符(gt - 大于、le - 小于或等于等)、变量名称和整数,并返回符合条件的单元格向量要求? 例如:

vector<Cells> resultCells = filter(myCells, gt, valueX, 5)

是单元格的向量,其中每个单元格的 valueX 大于 5。

我的第一次尝试需要很多 if 和 switch,我确信这不是正确的解决方案。 然后我向一个朋友要了一个小费,他告诉我诸如仿函数、lambdas、std::function、std::map、std::bind 之类的东西,这可以帮助我做到这一点,我已经读过它,但我我无法在实践中使用。

我在 Internet 上看到的一个示例是 this 之一,但它不那么复杂且难以重用(对我而言)。

【问题讨论】:

标签: c++ c++11 vector lambda


【解决方案1】:

使用std::copy_if 尝试这样的事情:

std::vector<Cell> resultCells;
std::copy_if(myCells.begin(), myCells.end(),
             std::back_inserter(resultCells),
             [](const Cell& c) { return c.valueX > 5; });

【讨论】:

  • 我真的希望使用 &lt;algorithm&gt; 的答案被勾选为“the”答案。
【解决方案2】:

保持简单:

for (auto& c : myCells)
    if (c.valueX > 5)
        resultCells.push_back(c);

根据您的特定要求(“编写一个函数,获取单元格向量、运算符(gt - 大于、le - 小于或等于等)、变量名和整数,并返回向量of Cells"),假设“get”表示“作为参数”:

vector<Cell> filter(vector<Cell> const& cells,
                    function<bool(int,int)> const& op,
                    int Cell::* var,
                    int i) {
    vector<Cell> results;
    for (auto& c : cells)
        if (op(c.*var, i))
            results.push_back(c);
    return results;
}

用法:

resultCells = filter(myCells, greater<int>(),  &Cell::valueX, 5);
resultCells = filter(myCells, less<int>(),     &Cell::valueY, 5);
resultCells = filter(myCells, equal_to<int>(), &Cell::ID,     5);

【讨论】:

    【解决方案3】:

    执行此操作的方法不止一种,具体取决于您是否要保留原始向量。

    在您的示例中,一种形式是使用copy_if 将元素从myCells 传输到resultCells。如果您有一个大容器并且之后不需要使用 myCells,这可能会很昂贵。在这种情况下,最好在 myCells 本身上使用remove_if

    第三种选择是使用 remove_copy_if 从 myCells 中删除,同时添加到 resultCells。

    【讨论】:

      【解决方案4】:

      在 C++14 中:

      template<class T, class A, class F>
      std::vector<T,A> keep_only( std::vector<T,A> input, F&& filter ) {
        auto it = std::remove_if( begin(input), end(input), [&](T const& t)->bool {
          return !filter(t);
        });
        input.erase(it, end(input));
        return input;
      }
      
      auto v2 = keep_only(v1, [](auto&& x){return x.valueX>5;}
      

      在 C++11 中,将 auto&amp;&amp; 替换为 Cell const&amp;

      此版本修改了它所采用的向量的副本。这意味着它复制每个元素,然后丢弃一些元素,并在丢弃过程中移动元素。这在您已经通过副本获取传入向量的情况下更为理想,因为它可以被省略/移动到。如果您的对象复制起来很昂贵并且您丢弃了大部分对象,那么这不是最佳选择。


      我们可以通过以下方式使keep_only 不受输入向量的限制:

      namespace details {
        template<class C, class F>
        auto keep_only( C&& input, F&& filter ) {
          using std::begin; using std::end;
          using T = std::decay_t<decltype(*begin(input))>;
      
          std::vector<T> retval;
          std::copy_if(
            begin(input), end(input),
            std::back_inserter(retval),
            std::forward<F>(filter)
          );
          return retval;
        }
      }
      template<class C, class F>
      auto keep_only( C&& input, F&& filter ) {
        return details::keep_only(std::forward<C>(input), std::forward<F>(filter));
      }
      

      并添加初始化列表支持:

      template<class T, class F>
      std::vector<T> keep_only( std::initializer_list<T> input, F&& filter ) {
        return details::keep_only(input, std::forward<F>(filter));
      }
      

      所以你可以输入keep_only( {1,2,3,4,5,6}, [](int x){return x&gt;1;} )

      live example

      这个版本只复制你告诉它保留的那些。它还会在矢量调整大小期间移动您平均保留一次的元素。无论输入如何,它总是返回一个向量。用 C++11 写这个可能不值得,因为正确推导 T 在函数上下文之外需要一些样板。

      您可以通过采用第一个实现并将签名更改为:

      template<class T, class A, class F>
      std::vector<T,A> keep_only( std::vector<T,A>&& input, F&& filter ) {
      

      并返回return std::move(input);:现在,对于除了临时向量之外的任何内容,我们构造一个向量并将内容复制到其中。对于临时向量,我们改为过滤掉我们不想要的部分。

      【讨论】:

        【解决方案5】:

        明确回答 OP 的要求,这里是所需的签名:

        vector&lt;Cells&gt; resultCells = filter(myCells, gt, valueX, 5)

        这是如何实现的:

        template<class T, class Pred, class ValueType>
        std::vector<T> filter(std::vector<T> result, Pred pred, ValueType T::* member, ValueType model)
        {
            result.erase(std::remove_if(std::begin(result),
                                        std::end(result),
                                        [&] (const auto& elem) {
                                            !return pred(elem.*member, model);
                                        }),
                         std::end(result));
            return result;
        }
        

        完整的工作示例(c++14 - c++11 需要更多的管道):

        #include <iostream>
        #include <vector>
        #include <utility>
        #include <algorithm>
        
        struct Cell {
            int ID;
            int valueX;
            int valueY;
        };
        
        template<class T, class Pred, class ValueType>
        std::vector<T> filter(std::vector<T> result, Pred pred, ValueType T::* member, ValueType model)
        {
            result.erase(std::remove_if(std::begin(result),
                                        std::end(result),
                                        [&] (const auto& elem) {
                                            return !pred(elem.*member, model);
                                        }),
                         std::end(result));
            return result;
        }
        
        int main()
        {
            auto myCells = std::vector<Cell> {
                { 0, 1, 2 },
                { 1, 2, 2 },
                { 2, 4, 2 },
                { 3, 6, 2 }
            };
            auto resultCells = filter(myCells, std::greater<>(), &Cell::valueX, 5);
            std::cout << resultCells.size() << std::endl;
            return 0;
        }
        

        预期输出:

        1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-24
          • 2022-11-30
          • 2019-10-30
          • 1970-01-01
          • 1970-01-01
          • 2019-01-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多