J'ai besoin de transformer un vecteur de chaînes en minuscules mais je dois préserver le cas des noms de fichiers. Ils sont identifiés par le jeton de chaîne précédent "file" ou "out".
{"col1", "col2", "file", "FileIn.dat", "out", "FileOut.dat"};
Dans le code ci-dessus, le résultat devrait être
#include <iostream> #include <algorithm> #include <vector> #include <iterator> template <class T> void print(const T& v) { std::copy(v.begin(), v.end(), std::ostream_iterator<typename T::value_type>(std::cout, "\n")); } std::string lowercase(const std::string& s) { std::string result(s); std::transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } int main() { std::vector<std::string> tokens {"Col1", "Col2", "File", "FileIn.dat", "Out", "FileOut.dat"}; std::transform(tokens.begin(), tokens.end(), tokens.begin(), lowercase); // how to replace lowercase() with a lambda that will take the previous // element into account while converting an element into lowercase print(tokens); return 0; }
préservant le cas de la chaîne après "file" et "out".
Y a-t-il un moyen de faire cela en utilisant les fonctions std :: transform
et lambda
?
3 Réponses :
Oui, il y en a. Vous pouvez utiliser un lambda de capture comme celui-ci:
col1 col2 file FileIn.dat out FileOut.dat
Sortie:
bool is_filename = false; std::transform(tokens.begin(), tokens.end(), tokens.begin(), [&is_filename] (auto &s) { if (is_filename) is_filename = false; else { s = lowercase (s); is_filename = s == "file" || s == "out"; } return s; });
std :: transform
garantit-il l'application dans l'ordre de l'opérateur unaire?
@Hiroki Pas selon cppreference, semble-t-il, mais leurs «implémentations possibles» le font. Je suppose qu'une boucle for
à distance serait plus sûre.
Oui. Vous pouvez capturer un état et le définir en fonction de ce que vous voyez. Cependant, vous ne devriez pas utiliser std :: transform
, car cela ne garantit pas la traversée dans l'ordre. Vous devez utiliser std::for_each
.
int main() { std::vector<std::string> tokens {"Col1", "Col2", "File", "FileIn.dat", "Out", "FileOut.dat"}; bool toggle = true; auto lowercase = [&toggle](std::string & s) { if (toggle) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); } toggle = (s == "file") || (s == "out"); } std::for_each(tokens.begin(), tokens.end(), tokens.begin(), lowercase); print(tokens); return 0; }
Y a-t-il un moyen de faire cela en utilisant les fonctions std :: transform et lambda?
std :: transform
ne garantit pas l'application dans l'ordre de l'opérateur. Ainsi, si nous devons appliquerstd :: transform
à ce problème, la surcharge pour l'opérateur binaire serait préférable:std::vector<std::string> tokens {"Col1", "Col2", "File", "FileIn.dat", "Out", "FileOut.dat"}; { // The first element must not be file name. tokens[0] = lowercase(tokens[0]); std::vector<std::string> dummy; std::transform( std::next(tokens.begin()), tokens.end(), tokens.begin(), std::back_inserter(dummy), [](auto& target, const auto& prev) { const auto prevLower = lowercase(prev); const auto isFileName = (prevLower == "file") || (prevLower == "out"); if(!isFileName){ target = lowercase(target); } return ""; }); } print(tokens);