C ++工具具有与Python的过滤器和映射相同的功能

时间:2014-02-18 21:38:51

标签: python c++ boost functional-programming

我正在寻找来自Python编程语言的mapfilter的C ++类比。第一个将函数应用于iterable的每个项目并返回结果列表,第二个从iterable的那些元素构造一个列表,其函数返回true

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

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

在C ++中是否有Python的地图和过滤器的精细实现?

在这个简短的例子中,我试图使用boost::bindstd::for_each这样的工具来解决这个问题,我遇到了困难。 std::vector<std::string> result应该包含所有字符串std::vector<std::string> raw,其字典值高于stdin中的最后一个字符串。但实际上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

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

正如@juanchopanza所建议的那样,<algorithm> 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

答案 1 :(得分:1)

您的代码无法正常工作的原因是bind()复制了所有参数。这意味着您要将项目添加到std::vector<string> 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; });

答案 2 :(得分:1)

要使当前代码正常工作,您必须将result参数包含在boost::bind boost::ref()内,否则bind将复制您的结果。

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

使用普通的C ++ 11标准库(即没有Boost),您可以通过将std::for_each()替换为以下内容来实现上述程序(请注意,不再需要filter_strings函数,您需要#include <iterator>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)比你在push_back中使用自定义for_each的初始方法更好,但它仍然看起来不太好。通常,可以使用Boost.Range编写更易读的代码,您可以在其中找到mapfilterfilteredtransformed的近1:1替换。对于上面的程序,这些不会特别有用,但特别是对于链式地图/过滤器,使用Boost.Range往往会有帮助。