Вектор преобразования строк: пропуск элементов на основе предыдущего значения - PullRequest
1 голос
/ 17 мая 2019

Мне нужно преобразовать вектор строк в нижний регистр, но мне нужно сохранить case имен файлов. Они идентифицируются по предыдущему строковому токену "file" или "out".

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

В приведенном выше коде результат должен быть

{"col1", "col2", "file", "FileIn.dat", "out", "FileOut.dat"};

сохранение регистра строки после «file» и «out».

Есть ли способ сделать это, используя функции std::transform и lambda?

Ответы [ 3 ]

2 голосов
/ 17 мая 2019

Да.Вы можете захватить какое-то состояние и установить его в зависимости от того, что вы видите.Однако вы не должны использовать std::transform, потому что это не гарантирует прохождение в порядке.Вы должны использовать 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;
}
2 голосов
/ 17 мая 2019

Да, есть.Вы можете использовать захват лямбды следующим образом:

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

Вывод:

col1
col2
file
FileIn.dat
out
FileOut.dat

Live демо

1 голос
/ 17 мая 2019

Есть ли способ сделать это, используя функции std :: transform и lambda?

std::transform не гарантирует применения оператора по порядку.Таким образом, если нам нужно применить std::transform к этой проблеме, перегрузка для двоичного оператора будет предпочтительнее:

Live Demo

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