【问题标题】:C++ tools with the same functionality as Python's filter and map与 Python 的过滤器和映射具有相同功能的 C++ 工具
【发布时间】:2014-03-18 22:15:11
【问题描述】:

我正在寻找来自 Python 编程语言的 mapfilter 的 C++ 类似物。它们中的第一个将一些函数应用于可迭代的每个项目并返回结果列表,第二个从函数返回true的迭代元素构造一个列表

我想在 C++ 中使用类似的功能:

  • 将一些函数映射到容器,以便获得具有转换后数据的新容器(并且可能具有不同的长度);
  • 对容器使用某种条件过滤;

C++ 中是否有 Python 的 map 和 filter 的良好实现?

在这个简短的示例中,我正在尝试使用boost::bindstd::for_each 等工具来解决这个问题,但我遇到了困难。 std::vector<std::string> result 应该包含所有字符串std::vector<std::string> raw,在字典上高于标准输入的最后一个字符串。但实际上result容器在返回点还是空的。

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>

void filter_strings(std::string& current, std::string& last, std::vector<std::string>& results)
{
    if (current > last)
    {
        results.push_back(current);
        std::cout << "Matched: " << current << std::endl;
    }
}

int main()
{
    std::vector<std::string> raw, result;
    std::string input, last;

    //Populate first container with a data
    while(std::getline(std::cin, input))
        raw.push_back(input);
    last = raw.back();

    //Put into result vector all strings which lexicographically higher than the last one
    std::for_each(raw.begin(), raw.end(), boost::bind(&filter_strings, _1, last, result));

    //For some reason the resulting container is empty
    std::cout << "Results: " << result.size() << std::endl;

    return 0;
}

输入和输出:

[vitaly@thermaltake 1]$ ./9_boost_bind 
121
123
122
120                      //Ctrl+D key press
Matched: 121
Matched: 123
Matched: 122
Results: 0

任何帮助将不胜感激。

【问题讨论】:

  • 你需要为在 std 容器上工作的良好通用 mapfilter 函数编写一些非常讨厌的模板,我尝试过一次但失败了。相反,您应该按照@juanchopanza 的建议使用std::algorithm
  • 看看 boost range,它导致的代码与 linq 和 python 非常相似

标签: python c++ boost functional-programming


【解决方案1】:

正如@juanchopanza 所建议的,&lt;algorithm&gt; STL 标头中的模板函数是您最好的选择。

#include <iostream>
#include <vector>


std::vector<std::string> filter(std::vector<std::string> & raw) {
    std::vector<std::string> result(raw.size());
    std::string last = raw[raw.size() - 1];
    auto it = std::copy_if(raw.begin(), raw.end(), result.begin(),
        [&](std::string s) { return s.compare(last) > 0; });
    result.resize(std::distance(result.begin(), it));
    return result;
}

int main(int argc, const char *argv[])
{
    std::vector<std::string> raw, result;
    std::string input;
    while (std::getline(std::cin, input)) {
        raw.push_back(input);
    }

    result = filter(raw);

    for (size_t i = 0; i < result.size(); i++) {
        std::cout << "Matched: " << result[i] << std::endl;
    }
    std::cout << "Results: " << result.size() << std::endl;
    return 0;
}

编译运行:

$ clang++ -std=c++11 -o cppfilter main.cpp && ./cppfilter
121
123
122
120  // Ctrl + D pressed
Matched: 121
Matched: 123
Matched: 122
Results: 3

【讨论】:

    【解决方案2】:

    要使您当前的代码工作,您必须将boost::bindresult 参数包装在boost::ref() 中,否则bind 将复制您的结果。

    否则,@juanchopanza 和 @alexbuisson 的评论者已经给出了很好的答案。

    使用普通的 C++11 标准库(即没有 Boost),您可以通过将 std::for_each() 替换为以下代码来实现上述程序(请注意,不再需要 filter_strings 函数,您需要 @ 987654329@std::back_inserter):

    std::copy_if(raw.begin(), raw.end(), std::back_inserter(result),
        [&](std::string const& current) -> bool {
            if (current > last)
            {
                std::cout << "Matched: " << current << std::endl;
                return true;
            }
            return false;
        }
    );
    

    虽然这(可能,如果您知道 STL)比您在 for_each 中使用自定义 push_back 的初始方法更好,但它看起来仍然不是很好。通常,可以使用 Boost.Range 编写更具可读性的代码,您可以在其中找到 mapfilter 的近 1:1 替换:filteredtransformed。对于上面的程序,这些不会特别有用,但特别是对于链式映射/过滤器,使用 Boost.Range 往往会有所帮助。

    【讨论】:

      【解决方案3】:

      您的代码没有像您想象的那样工作的原因是bind() 复制了所有参数。这意味着您正在将项目添加到您的 std::vector&lt;string&gt; result; 的副本中 要解决此问题,您需要将向量放入参考包装器中。然后将其复制,但它包含对您的 result 向量的引用。变化很小:

      std::for_each(raw.begin(), raw.end(), std::bind(&filter_strings, std::placeholders::_1, last, std::ref(result)));
      

      请注意,我在这里使用的是 C++11 绑定,而不是 boost 绑定。​​

      现在,如果您想使用 lambda 将谓词代码保持在过滤器的本地,您可以:

      std::for_each(raw.begin(), raw.end(), [&](std::string& s){ if (s > last) result.push_back(s); });
      

      或者使用std::copy_if:

      std::copy_if(raw.begin(), raw.end(), std::back_inserter(result), [&](std::string& s){ return s > last; });
      

      【讨论】:

        猜你喜欢
        • 2013-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多