【问题标题】:Transform vector of strings : skip elements based on the previous value字符串变换向量:根据前一个值跳过元素
【发布时间】:2019-10-04 23:44:28
【问题描述】:

我需要将字符串向量转换为小写,但我需要保留文件名的 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::transformlambda 函数来做到这一点?

【问题讨论】:

    标签: c++ c++11 lambda stl


    【解决方案1】:

    是的,有。您可以像这样使用捕获 lambda:

    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 demo

    【讨论】:

    • std::transform 是否保证一元运算符的有序应用?
    • @Hiroki 似乎不是根据 cppreference,但他们的“可能的实现”可以。我猜一个远程for 循环会更安全。
    【解决方案2】:

    是的。您可以捕获一些状态,并根据您所看到的进行设置。但是你不应该使用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;
    }
    

    【讨论】:

      【解决方案3】:

      有没有办法使用 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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-28
        • 2014-05-07
        相关资源
        最近更新 更多