c++中的类python映射

Python-like map in C++

本文关键字:python 映射 c++      更新时间:2023-10-16

我的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;
}

下面是我打算如何使用这个的一个例子(其中过滤器的返回类型是它的第一个参数的类型):

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;

让你的代码更通用一点:

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中使上述工作,您必须添加末尾返回类型-> 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>;

需要移动到helper类型

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中,我发现咖喱风格工作得很好

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 );

另一方面,也许总是生成向量可能是一个值得的简化。然而,总是只使用向量似乎没有意义。

不使用U作为模板参数,您可以简单地像这样声明结果向量:

std::vector<typename std::result_of<UnaryFunction(T)>::type>