如何使用范围 v3 范围实现平面地图
How to implement flatmap using rangev3 ranges
我有一个非常简单的flatmap
函数在C++中实现std::vector
,但有人建议范围通常更好。这是基于矢量的解决方案:
// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};
也有人建议我使用迭代器,但这破坏了函数的良好可组合性:
map(filter(flatmap( V, fn), fn2), fn3)
我假设在范围 v3 世界中,我的目标是将上述内容编写为:
auto result = v | flatmap(fn) | filter(fn2) | transform(fn3);
感觉flatmap
应该只是views::for_each
、yield_from
和transform
的微不足道的组合,但我正在努力弄清楚如何将它们连接在一起。
IIUC,您的flatmap
函数只不过是range-v3的view::for_each
。尝试:
using namespace ranges;
auto result = v | view::for_each(fn) | to_vector;
呵
如果我理解正确,你的函数flatmap
必须做什么,你可以把它写成v | view::transform(fn) | action::join
。以下是使用范围制作的示例:
#include <range/v3/all.hpp>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
// flatmap: [A] -> (A->[B]) -> [B]
template<typename T, typename FN>
static auto flatmap(const std::vector<T> &vec, FN fn)
-> std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> {
std::vector<typename std::remove_reference<decltype(fn(T())[0])>::type> result;
for(auto x : vec) {
auto y = fn(x);
for( auto v : y ) {
result.push_back(v);
}
}
return result;
};
// This will be test function for both flatmap and range usage
std::vector<std::string> testFn(int n)
{
std::vector<std::string> result;
int ofs = 0;
for(int i = 0; i < n; ++i)
{
char initialChar = 'A' + ofs;
std::string partialResult = """;
for(int j = 0; j <=i; ++j, ++ofs)
{
partialResult.append(1, initialChar+j);
}
partialResult += """;
result.push_back(partialResult);
}
return std::move(result);
}
int main(int, char**)
{
std::vector<int> vv {1, 2, 3, 4, 5, 6};
// test flatmap
auto r2 = flatmap(vv, testFn);
for(auto s:r2)
{
std::cout << s << " " ;
}
std::cout << "n";
using namespace ranges;
// test ranges equivalent
auto rng = vv|view::transform(testFn)|action::join;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is an equivalent for flatmap in ranges terms
for(auto s:rng)
{
std::cout << s << " ";
}
std::cout << "n";
std::cout << std::flush;
return 0;
}
两个答案都是正确的,但我想添加更多的上下文,因为for_each的命名可能有点令人困惑(它确实让我感到困惑)。下面是一个示例,可用于验证view::for_each
实际上是范围的 flatMap:
#include <range/v3/all.hpp>
#include <iostream>
#include <vector>
using namespace ranges;
int main()
{
const std::vector<int> a = { 0, 1, 2 };
auto b = a | view::for_each([] (int x) { return view::ints(x, x+3); });
ranges::for_each( b, [] (int x) { std::cout << x << " "; } );
std::cout << std::endl;
}
这将打印0 1 2 1 2 3 2 3 4
。该示例还显示了混淆的可能来源,因为实际上在 range 命名空间中有一个函数for_each其功能类似于 Java 的 forEach(即将函数应用于没有返回值的范围的每个成员)。
如果您查看view::for_each
的文档,您会发现它是使用 transform 和 join 间接实现的。
auto operator() (Rng &&rng, Fun fun) const -> decltype(join(transform(static_cast< Rng &&>(rng), std::move(fun))))
相关文章:
- 如果没有malloc,链表实现将失败
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 实现无开销push_back的最佳方法是什么
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 我应该实现右值推送功能吗?我应该使用std::move吗
- 如何正确实现和访问运算符的各种自定义枚举器
- C++Union/Struct位域的实现和可移植性
- 这个极客对极客的trie实现是否存在内存泄漏问题
- 在c++中实现LinkedList时,应出现未处理的错误
- 为左值和右值的包装器实现C++范围
- 使用模板进行堆栈实现; "name followed by :: must be a class or namespace"
- 使用GSoap实现ONVIF
- 在用于格式4的arm模拟器中实现功能时的一个问题
- 用于AVX的ln(x)的实现,m256
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 如何使用范围 v3 范围实现平面地图
- 是否有一个平面的未排序映射/集实现