Fonction de composition de fonction de type Haskell
Tout comme cette question, j'essaie d'écrire en C ++ quelque chose qui peut ressembler (.)autant que possible à la fonction de Haskell , qui est essentiellement le deuxième exercice du chapitre 1 de la théorie des catégories pour les programmeurs de Bartosz Milewski .
Cette question similaire sur l'écriture de la fonction d' identité a également servi de référence.
Cette réponse sur StackOverflow m'a rassuré que decltypeutilisé sur les autoparamètres de lambdas est équivalent à decltypeutilisé sur les paramètres de modèle.
Et voici ce que j'ai proposé:
static constexpr struct compose_t {
template <typename F, typename G>
constexpr decltype(auto) operator()(F&& f, G&& g) const noexcept {
return [&f,&g](auto&& x){
return f(g(std::forward<decltype(x)>(x)));
};
}
} compose;
Quelques idées:
operator()prendfetgpar référence, et le lambda retourné les capture par référence; et j'ai fait ce choix en pensant que cela devrait rendre le passage des appels non apatrides moins chers, mais je ne suis pas sûr d'avoir fait le bon choix . Une autre référence est celle-ci .- Depuis que je les passe par référence, j'ai dû choisir entre
const&et&&, et j'ai essentiellement choisi au hasard. - Je ne me suis pas préoccupé des variadiques, car je voulais un "opérateur" de composition de fonction binaire , en ligne avec celui de Haskell
(.) :: (b -> c) -> (a -> b) -> a -> c. - Je ne peux pas composer
composeavec autre chose, car c'est binaire, alors que l'implémentation suppose implicitement celaFetGest unaire. Je suppose que faire quelque chose commecompose(compose,compose)(qui est(.).(.)dans Haskell) possible serait jusqu'à une "facilité" d'application partielle.
C'est un code où j'ai essayé de le tester:
#include <cassert>
#include <utility>
// ... the code from above
static constexpr struct changesign_t {
template<typename T>
constexpr decltype(auto) operator()(T&& t) const noexcept { return -t; };
} changesign;
int square(int x) { return x*x; }
int twice(int x) { return 2*x; }
int main() {
assert(compose(square,twice)(3) == 36);
assert(compose([](int x){ return x*x; },[](int x){ return 2*x; })(3) == 36);
assert(compose(changesign, compose([](auto x){ return x*x; }, [](auto x){ return 2*x; }))(3) == -36);
}
Réponses
Il n'est pas nécessaire d'utiliser un struct compose_tpour envelopper operator(), vous pouvez simplement en operator()sortir et le renommer compose():
template <typename F, typename G>
constexpr decltype(auto) compose(F&& f, G&& g) noexcept {
return [&f,&g](auto&& x){
return f(g(std::forward<decltype(x)>(x)));
};
}
Maintenant, il est également beaucoup plus proche du code écrit dans ce post que vous avez mentionné , sauf bien sûr que vous êtes limité à ne composer que deux fonctions unaires.
À part cela, si vous pouvez vivre avec les restrictions que vous avez, je ne vois rien de mal à votre implémentation.