1
votes

Transformer le vecteur de chaînes: ignorer les éléments en fonction de la valeur précédente

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 ?


0 commentaires

3 Réponses :


2
votes

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

Démo en direct


2 commentaires

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.



2
votes

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


0 commentaires

1
votes

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 appliquer std :: transform à ce problème, la surcharge pour l'opérateur binaire serait préférable:

Démo en direct

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

0 commentaires