1
votes

Comment appliquer une liste de fonctions à une chaîne en C ++?

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


4 commentaires

Pour enchaîner les fonctions membres, ces fonctions doivent renvoyer une référence à * this ou à un type de proxy qui se comporte de manière similaire. Si inputString est une std :: string alors il n'y a pas vraiment de fonctions membres qui font cela sauf operator = et assign < / 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 pour l'obtenir.


7 Réponses :


1
votes

Comme ceci:

auto outPutString = Transformation2(Transformation1(inputString));


0 commentaires

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

2 commentaires

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



0
votes

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.


1 commentaires

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.



1
votes

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


0 commentaires

0
votes

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>


0 commentaires

2
votes

À 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 .


0 commentaires

0
votes
#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
*/

0 commentaires