J'ai une liste de fonctions qui doivent être appliquées à une seule chaîne de manière additive. Comment exprimer la fonction "Apply".
auto outPutString = inputString .Apply(Transformation1) .Apply(Transformation2)
en c ++?
La chaîne est la chaîne std :: string
7 Réponses :
Comme ceci:
auto outPutString = Transformation2(Transformation1(inputString));
std::string manipulateString(std::string str) {/* do something */; return result;}
std::string manipulateStringAgain(std::string str) {/* do something else */; return result;}
std::string manipulateMe = "hello";
auto resultString = manipulateString(manipulateStringAgain(manipulateMe));
Vous avez l'ordre à l'envers, c'est pourquoi la façon dont l'OP le fait est tellement meilleure. manipulateStringAgain est en fait appelé en premier là où avec la façon dont vous l'avez nommé, il devrait être appelé en dernier.
@NathanOliver vous avez raison, c'est un avantage. J'ai ajouté une autre réponse pour répondre à cette préoccupation
Il est également possible en C et C ++ de définir un pointeur vers une fonction et de créer un vecteur de pointeurs vers des fonctions. Plus tard, vous pouvez appeler des fonctions à l'intérieur d'une boucle avec les arguments souhaités. Veuillez me faire savoir si vous êtes intéressé pour plus de détails.
Oui, veuillez ajouter des détails. L'OP aimerait auto outPutString = inputString .Apply (Transformation1) .Apply (Transformation2) et je ne vois pas que votre proposition les y permettrait.
Je vais supposer que lorsque vous dites "une liste de fonctions", vous en voulez une qui varie au moment de l'exécution. D'autres réponses sont meilleures si la liste est statique.
#include <vector>
#include <string>
#include <functional>
#include <numeric>
std::vector<std::function<std::string(std::string)>> funcs = { Transformation1, Transformation2 }; // or gotten from wherever
auto output = std::accumulate(funcs.begin(), funcs.end(), input, [](auto acc, auto fun){ return fun(acc); });
Si vous souhaitez conserver l'ordre, créez une classe d'emballage et placez-y vos fonctions de manipulation. Par exemple:
#include <iostream>
#include <string>
using namespace std;
class StringManipulator
{
public:
StringManipulator(std::string str) : str(str) {}
operator std::string() {return str;}
StringManipulator& doSomething() {str += "1"; return *this;}
StringManipulator& doSomethingElse() {str += "2"; return *this;}
private:
std::string str;
};
int main() {
std::string result = StringManipulator("0").doSomething().doSomethingElse();
std::cout << result;
return 0;
}
La sortie est 012.
opérateur std :: string assure une conversion implicite. p>
À partir de C ++ 11, vous pouvez également écrire une fonction Apply en utilisant des modèles variadiques:
auto res = Apply<size_t>(
"Hello",
[](const std::string &str) { return str + " World"; }, // Applicator 1
[](const std::string &str) { return str.size(); } // Applicator 2
);
Ensuite, vous pouvez utiliser ceci comme suit:
XXX
Le résultat dans ce cas est  »Hello World«.
Parce que la construction ci-dessus fait la distinction entre InputT et OutputT code>, vous pouvez "mélanger" les types, comme dans:
auto res = Apply<std::string>(
"Hello",
[](const std::string &str) { return str + " "; }, // Applicator 1
[](const std::string &str) { return str + "World"; } // Applicator 2
);
Cette fois, le résultat est 11.
Enfin , si vous voulez vraiment utiliser la syntaxe de chaînage, vous pouvez écrire une classe qui encapsule l'objet initial et possède une méthode Apply .
#include <vector>
#include <iostream>
// three funcitons with a string as the parameter
int funca(std::string& str)
{
std::cout << "funca:" << str << std::endl;
return 1;
}
int funcb(std::string& str)
{
std::cout << "funcb:" << str << std::endl;
return 2;
}
int funcd(std::string& str)
{
std::cout << "funcd:" << str << std::endl;
return 3;
}
int main()
{
// definition of the string
std::string str = "the string";
// declare vector of pointers to function returning an int and receiving a string as a parameter:
std::vector< int(*)(std::string&)> pf;
// load func pointers to vector:
pf.push_back(&funca);
pf.push_back(&funcb);
pf.push_back(&funcd);
//declare vector iterator:
std::vector<int (*)(std::string&)>::iterator it;
// iterate vector of func pointers:
for (it = pf.begin() ; it != pf.end(); ++it)
{
// function call using pointers and passing parameter str
// you can get return value as from 'normal' function
int ret = (*it)(str);
std::cout << "function returns:" << ret << std::endl;
}
}
/*
compiled and executed on ubuntu 18.04, output:
funca:the string
function returns:1
funcb:the string
function returns:2
funcd:the string
function returns:3
*/
Pour enchaîner les fonctions membres, ces fonctions doivent renvoyer une référence à
* thisou à un type de proxy qui se comporte de manière similaire. SiinputStringest unestd :: stringalors il n'y a pas vraiment de fonctions membres qui font cela saufoperator =etassign < / code>.Cette chaîne est-elle votre propre type ou une
std :: string?Il y avait une fois une question en quelque sorte similaire qui pourrait être intéressante: SO: Opérateur de flux entièrement personnalisé en c ++ .
J'ai aimé écrire du code comme ci-dessus en C #. C'est beaucoup plus naturel que la façon dont nous devons le faire en c ++. Cela dit, il existe une proposition ouverte a> pour l'obtenir.