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

C++ tools with the same functionality as Python's filter and map

本文关键字:过滤器 映射 功能 Python 工具 C++      更新时间:2023-10-16

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

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

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

Python的映射和过滤器在C++中有什么好的实现吗?

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

任何帮助都将不胜感激。

正如@juancopanza所建议的,<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

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

除此之外,评论者@juancopanza和@alexbuisson已经对此给出了很好的答案。

使用普通的C++11标准库(即没有Boost),您可以通过用以下内容替换std::for_each()来实现上述程序(注意,filter_strings函数不再需要,std::back_inserter需要#include <iterator>):

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编写可读性更强的代码,在这里可以找到几乎1:1的mapfilter的替代品:filteredtransformed。对于上面的程序,这些不会特别有帮助,但特别是对于链式映射/过滤器,使用Boost.Range往往会有所帮助。

您的代码没有像您想象的那样工作的原因是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; });