Python-类似于 C++ 中的地图
Python-like map in C++
我的 std::transform
问题是我无法同时获取临时对象的开头和结尾。
我想在 C++ 中实现一个类似 Python 的映射函数,它作用于一种类型的向量并将它们映射到另一个向量(可能是另一种类型)。
这是我的方法:
template <class T, class U, class UnaryOperator>
std::vector<T> map(const std::vector<T>& vectorToMap, UnaryOperator operation)
{
std::vector<U> result;
result.reserve(vectorToMap.size());
std::transform(vectorToMap.begin(), vectorToMap.end(),
std::back_inserter(result), [&operation] (U item) { return operation(item); });
return result;
}
这是我打算如何使用它的示例(其中 return 类型的过滤器是其第一个参数的类型):
std::vector<std::shared_ptr<Cluster>> getClustersWithLength(const std::vector<Cluster>& clusterCollection, const int& length)
{
return map(filter(clusterCollection, [&length] (Cluster& cluster) {
return cluster.sizeY == length;
}),
[] (const Cluster& cluster) {
return std::make_shared<Cluster>(cluster);
});
}
我收到的关于此代码的错误消息是:
error: no matching function for call to 'map(std::vector<Cluster>,
ClusterPairFunctions::getClustersWithLength(const
std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>)'
note: candidate: template<class T, class U, class UnaryOperator> std::vector<_RealType> map(const std::vector<_RealType>&, UnaryOperator)
std::vector<T> map(const std::vector<T>& vectorToMap, UnaryOperator operation)
note: couldn't deduce template parameter 'U'
你能帮我一些忙吗,我该如何解决?另外,我能否以某种方式使用编译时静态断言来检查操作类型 (T t) 是否为 U?
删除 U
并将结果声明替换为 std::vector<typename std::result_of<UnaryFunction(T)>::type> result;
仍然会产生错误:
src/ClusterPairFunctions.cc: In function 'std::vector<std::shared_ptr<Cluster> > ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)':
src/ClusterPairFunctions.cc:130:14: error: could not convert 'map(const std::vector<_RealType>&, UnaryFunction) [with T = Cluster; UnaryFunction = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>]((<lambda closure object>ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>{}, ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>()))' from 'std::vector<Cluster>' to 'std::vector<std::shared_ptr<Cluster> >'
return (map(filter(clusterCollection, [&length] (Cluster& cluster) {
In file included from src/../interface/ClusterPairFunctions.h:5:0,
from src/ClusterPairFunctions.cc:1:
src/../interface/../../../interface/HelperFunctionsCommon.h: In instantiation of 'std::vector<_RealType> filter(const std::vector<_RealType>&, UnaryPredicate) [with T = Cluster; UnaryPredicate = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)>]':
src/ClusterPairFunctions.cc:132:4: required from here
src/../interface/../../../interface/HelperFunctionsCommon.h:52:15: error: no match for call to '(ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)>) (const Cluster&)'
if(predicate(*it)) result.push_back(*it);
^
src/ClusterPairFunctions.cc:130:68: note: candidate: ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)> <near match>
return (map(filter(clusterCollection, [&length] (Cluster& cluster) {
^
src/ClusterPairFunctions.cc:130:68: note: conversion of argument 1 would be ill-formed:
In file included from src/../interface/ClusterPairFunctions.h:5:0,
from src/ClusterPairFunctions.cc:1:
src/../interface/../../../interface/HelperFunctionsCommon.h:52:15: error: binding 'const Cluster' to reference of type 'Cluster&' discards qualifiers
if(predicate(*it)) result.push_back(*it);
^
src/../interface/../../../interface/HelperFunctionsCommon.h: In instantiation of 'std::vector<_RealType> map(const std::vector<_RealType>&, UnaryFunction) [with T = Cluster; UnaryFunction = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>]':
src/ClusterPairFunctions.cc:135:4: required from here
src/../interface/../../../interface/HelperFunctionsCommon.h:64:9: error: could not convert 'result' from 'std::vector<std::shared_ptr<Cluster> >' to 'std::vector<Cluster>'
return result;
您可以像这样简单地声明结果向量,而不是将 U
作为模板参数:
std::vector<typename std::result_of<UnaryFunction(T)>::type>
这是您的代码,更加通用:
template <template<class...>class Z=std::vector, class C, class UnaryOperator>
auto fmap(C&& c_in, UnaryOperator&& operation)
{
using dC = std::decay_t<C>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R = Z<T_out>;
R result;
result.reserve(vectorToMap.size());
using std::begin; using std::end;
std::transform(
begin(cin), end(cin),
std::back_inserter(result),
[&] (auto&& item) { return operation(declype(item)(item)); }
);
return result;
}
要在 C++11 中完成上述工作,您必须添加尾随 return 类型 -> decltype(complex expression)
并将漂亮的 std::decay_t<whatever>
替换为 typename std::decay<whatever>::type
或写你自己的别名。
这些步骤:
using dC = std::decay<C>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R = Z<T_out>;
需要移动到助手类型
template<template<class...>class Z, class C, class Op>
struct calculate_return_type {
using dC = typename std::decay<C>::type;
using T_in = typename dC::reference;
using T_out = typename std::decay< typename std::result_of< Op&(T_in) >::type >::type;
using R = Z<T_out>;
};
给我们这个:
template <template<class...>class Z=std::vector, class C, class UnaryOperator>
auto fmap(C&& c_in, UnaryOperator&& operation)
-> typename calculate_return_type<Z, C, UnaryOperator>::R
{
using R = typename calculate_return_type<Z, C, UnaryOperator>::R;
R result;
result.reserve(c_in.size());
using T_in = typename calculate_return_type<Z, C, UnaryOperator>::T_in;
using std::begin; using std::end;
std::transform(
begin(c_in), end(c_in),
std::back_inserter(result),
[&] (T_in item) { return operation(decltype(item)(item)); }
);
return result;
}
但实际上,现在是 2016 年,请尝试升级到 C++14。
在 C++14 中,我发现 curry-style 效果很好
template<class Z, class T>
struct rebind_helper;
template<template<class...>class Z, class T_in, class...Ts, class T_out>
struct rebind_helper<Z<T_in,Ts...>, T_out> {
using type=Z<T_out, Ts...>;
};
template<class Z, class T>
using rebind=typename rebind_helper<Z,T>::type;
template<class Op>
auto fmap( Op&& op ) {
return [op = std::forward<Op>(op)](auto&& c) {
using dC = std::decay_t<decltype(c)>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R=rebind< dC, T_out >;
R result;
result.reserve(vectorToMap.size());
using std::begin; using std::end;
std::transform(
begin(cin), end(cin),
std::back_inserter(result),
[&] (auto&& item) { return operation(declype(item)(item)); }
);
return result;
};
}
这两个都需要一个 "reserve if possible" 函数(SFINAE 会检测 .reserve
是否存在,如果存在则保留;否则,不打扰)。
第二个看起来像:
auto fmap_to_double = fmap( [](auto in){ return (double)in; } );
然后可以将其传递给容器并将其元素重新映射到 double
。
auto double_vector = fmap_to_double( int_vector );
另一方面,也许始终生成向量可能是一种值得简化的方法。然而,总是只消耗向量似乎毫无意义。
我的 std::transform
问题是我无法同时获取临时对象的开头和结尾。
我想在 C++ 中实现一个类似 Python 的映射函数,它作用于一种类型的向量并将它们映射到另一个向量(可能是另一种类型)。
这是我的方法:
template <class T, class U, class UnaryOperator>
std::vector<T> map(const std::vector<T>& vectorToMap, UnaryOperator operation)
{
std::vector<U> result;
result.reserve(vectorToMap.size());
std::transform(vectorToMap.begin(), vectorToMap.end(),
std::back_inserter(result), [&operation] (U item) { return operation(item); });
return result;
}
这是我打算如何使用它的示例(其中 return 类型的过滤器是其第一个参数的类型):
std::vector<std::shared_ptr<Cluster>> getClustersWithLength(const std::vector<Cluster>& clusterCollection, const int& length)
{
return map(filter(clusterCollection, [&length] (Cluster& cluster) {
return cluster.sizeY == length;
}),
[] (const Cluster& cluster) {
return std::make_shared<Cluster>(cluster);
});
}
我收到的关于此代码的错误消息是:
error: no matching function for call to 'map(std::vector<Cluster>,
ClusterPairFunctions::getClustersWithLength(const
std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>)'
note: candidate: template<class T, class U, class UnaryOperator> std::vector<_RealType> map(const std::vector<_RealType>&, UnaryOperator)
std::vector<T> map(const std::vector<T>& vectorToMap, UnaryOperator operation)
note: couldn't deduce template parameter 'U'
你能帮我一些忙吗,我该如何解决?另外,我能否以某种方式使用编译时静态断言来检查操作类型 (T t) 是否为 U?
删除 U
并将结果声明替换为 std::vector<typename std::result_of<UnaryFunction(T)>::type> result;
仍然会产生错误:
src/ClusterPairFunctions.cc: In function 'std::vector<std::shared_ptr<Cluster> > ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)':
src/ClusterPairFunctions.cc:130:14: error: could not convert 'map(const std::vector<_RealType>&, UnaryFunction) [with T = Cluster; UnaryFunction = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>]((<lambda closure object>ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>{}, ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>()))' from 'std::vector<Cluster>' to 'std::vector<std::shared_ptr<Cluster> >'
return (map(filter(clusterCollection, [&length] (Cluster& cluster) {
In file included from src/../interface/ClusterPairFunctions.h:5:0,
from src/ClusterPairFunctions.cc:1:
src/../interface/../../../interface/HelperFunctionsCommon.h: In instantiation of 'std::vector<_RealType> filter(const std::vector<_RealType>&, UnaryPredicate) [with T = Cluster; UnaryPredicate = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)>]':
src/ClusterPairFunctions.cc:132:4: required from here
src/../interface/../../../interface/HelperFunctionsCommon.h:52:15: error: no match for call to '(ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)>) (const Cluster&)'
if(predicate(*it)) result.push_back(*it);
^
src/ClusterPairFunctions.cc:130:68: note: candidate: ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(Cluster&)> <near match>
return (map(filter(clusterCollection, [&length] (Cluster& cluster) {
^
src/ClusterPairFunctions.cc:130:68: note: conversion of argument 1 would be ill-formed:
In file included from src/../interface/ClusterPairFunctions.h:5:0,
from src/ClusterPairFunctions.cc:1:
src/../interface/../../../interface/HelperFunctionsCommon.h:52:15: error: binding 'const Cluster' to reference of type 'Cluster&' discards qualifiers
if(predicate(*it)) result.push_back(*it);
^
src/../interface/../../../interface/HelperFunctionsCommon.h: In instantiation of 'std::vector<_RealType> map(const std::vector<_RealType>&, UnaryFunction) [with T = Cluster; UnaryFunction = ClusterPairFunctions::getClustersWithLength(const std::vector<Cluster>&, const int&)::<lambda(const Cluster&)>]':
src/ClusterPairFunctions.cc:135:4: required from here
src/../interface/../../../interface/HelperFunctionsCommon.h:64:9: error: could not convert 'result' from 'std::vector<std::shared_ptr<Cluster> >' to 'std::vector<Cluster>'
return result;
您可以像这样简单地声明结果向量,而不是将 U
作为模板参数:
std::vector<typename std::result_of<UnaryFunction(T)>::type>
这是您的代码,更加通用:
template <template<class...>class Z=std::vector, class C, class UnaryOperator>
auto fmap(C&& c_in, UnaryOperator&& operation)
{
using dC = std::decay_t<C>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R = Z<T_out>;
R result;
result.reserve(vectorToMap.size());
using std::begin; using std::end;
std::transform(
begin(cin), end(cin),
std::back_inserter(result),
[&] (auto&& item) { return operation(declype(item)(item)); }
);
return result;
}
要在 C++11 中完成上述工作,您必须添加尾随 return 类型 -> decltype(complex expression)
并将漂亮的 std::decay_t<whatever>
替换为 typename std::decay<whatever>::type
或写你自己的别名。
这些步骤:
using dC = std::decay<C>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R = Z<T_out>;
需要移动到助手类型
template<template<class...>class Z, class C, class Op>
struct calculate_return_type {
using dC = typename std::decay<C>::type;
using T_in = typename dC::reference;
using T_out = typename std::decay< typename std::result_of< Op&(T_in) >::type >::type;
using R = Z<T_out>;
};
给我们这个:
template <template<class...>class Z=std::vector, class C, class UnaryOperator>
auto fmap(C&& c_in, UnaryOperator&& operation)
-> typename calculate_return_type<Z, C, UnaryOperator>::R
{
using R = typename calculate_return_type<Z, C, UnaryOperator>::R;
R result;
result.reserve(c_in.size());
using T_in = typename calculate_return_type<Z, C, UnaryOperator>::T_in;
using std::begin; using std::end;
std::transform(
begin(c_in), end(c_in),
std::back_inserter(result),
[&] (T_in item) { return operation(decltype(item)(item)); }
);
return result;
}
但实际上,现在是 2016 年,请尝试升级到 C++14。
在 C++14 中,我发现 curry-style 效果很好
template<class Z, class T>
struct rebind_helper;
template<template<class...>class Z, class T_in, class...Ts, class T_out>
struct rebind_helper<Z<T_in,Ts...>, T_out> {
using type=Z<T_out, Ts...>;
};
template<class Z, class T>
using rebind=typename rebind_helper<Z,T>::type;
template<class Op>
auto fmap( Op&& op ) {
return [op = std::forward<Op>(op)](auto&& c) {
using dC = std::decay_t<decltype(c)>;
using T_in = dC::reference;
using T_out = std::decay_t< std::result_of_t< UnaryOperator&(T_in) > >;
using R=rebind< dC, T_out >;
R result;
result.reserve(vectorToMap.size());
using std::begin; using std::end;
std::transform(
begin(cin), end(cin),
std::back_inserter(result),
[&] (auto&& item) { return operation(declype(item)(item)); }
);
return result;
};
}
这两个都需要一个 "reserve if possible" 函数(SFINAE 会检测 .reserve
是否存在,如果存在则保留;否则,不打扰)。
第二个看起来像:
auto fmap_to_double = fmap( [](auto in){ return (double)in; } );
然后可以将其传递给容器并将其元素重新映射到 double
。
auto double_vector = fmap_to_double( int_vector );
另一方面,也许始终生成向量可能是一种值得简化的方法。然而,总是只消耗向量似乎毫无意义。