2

Mein Problem mit std::transform ist, dass ich nicht beide den Anfang und das Ende eines temporären Objekts erhalten kann.Python-ähnliche Karte in C++

Ich möchte eine Python-ähnliche Mapping-Funktion in C++ implementieren, die auf Vektoren eines Typs arbeitet und sie auf einen anderen Vektor (möglicherweise eines anderen Typs) abbildet.

Das ist mein Ansatz:

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

Und dies ist ein Beispiel dafür, wie ich beabsichtige, diese zu verwenden (wobei der Rückgabetyp des Filters die Art ihres ersten Arguments ist):

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

Die Fehlermeldung, die ich für diesen Code zu bekommen ist jedoch:

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' 

können Sie mir etwas Hilfe geben, wie kann ich es beheben? Kann ich irgendwie die statische Assertion zur Kompilierungszeit verwenden, um zu überprüfen, ob der Operationstyp (T t) U ist?

Entfernen U und ersetzt die Erklärung des Ergebnisses mit std::vector<typename std::result_of<UnaryFunction(T)>::type> result; noch erzeugt einen Fehler:

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; 

Antwort

3

Hier ist Ihr Code ein wenig generischer:

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

Um das oben genannte in C++ 11 zu arbeiten, müssen Sie nachgestellten Rückgabetyp -> decltype(complex expression) hinzufügen und ersetzen Sie die nette std::decay_t<whatever> durch typename std::decay<whatever>::type oder schreiben Sie Ihre eigenen Aliase.

Diese Schritte:

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

Notwendigkeit zu einem Helfer-Typ

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

geben uns diese bewegt werden: es

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

aber wirklich, ist 2016 tun Versuch, Upgrade auf C++ 14.

Live example


In C++ 14 finde ich Curry-Stil funktioniert gut

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

diese beiden eine "Reserve, wenn möglich" benötigen Funktion (die SFINAE tut, um festzustellen, ob .reserve existiert, und wenn so reserviert, sonst stört es nicht).

Die zweite sieht aus wie:

auto fmap_to_double = fmap([](auto in){ return (double)in; }); 

, die dann einen Behälter geleitet werden kann und es ordnet seine Elemente zu double.

auto double_vector = fmap_to_double(int_vector); 

Auf der anderen Seite, vielleicht immer Vektoren zu produzieren, könnte eine sinnvolle Vereinfachung sein. Es erscheint jedoch immer sinnlos, nur Vektoren zu konsumieren.

+0

Dieser Code ist in C++ 11 nicht gültig. –

+0

@AdamHunyadi Verbose C++ 11 Version hinzugefügt. – Yakk

+1

@AdamHunyadi Tippfehler und restliche C++ 14 Sachen entfernt, Live-Beispiel enthalten. – Yakk

1

Statt U als Template-Parameter haben, können Sie einfach Ihre Ergebnisvektor wie folgt erklären:

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

Dies erzeugt immer noch eine Fehlermeldung. Ich habe es als Bearbeitung zu der Frage hinzugefügt. –