对字符串的操作一直被认为是程序员的基本功之一。对于一个英文的字符串来说,最简单的操作,就是进行大小写转换了。这不是什么难事,但这里我们讨论的是 C++ 风格的写法。
std::transform
std::transform
是定义在头文件 algorithm
当中的一个函数模板。它和标准库中大多数其他函数模板一样,是对迭代器进行操作的函数。在 C++11 中,它有两个函数签名。
1 | template <typename InputIt, |
从功能上说,std::transform
和 Python 当中的内建函数 map()
非常相似。
(1) 接收两个 InputIt
类型的迭代器,界定了待处理的元素的范围(左闭右开区间),被一元操作 unary_op
处理之后,依次保存在 OutputIt
对应的容器当中。这基本上就是 Python 当中的 map(lambda x: return <do_something>, <iterable>)
。只不过,Python 当中的 map()
将结果作为返回值返回,而 std::transform
将结果保存在 d_first
对应的容器中。
有了 (1) 的知识,(2) 也就不难理解了。(2) 的输入接受两组迭代器。第一组迭代器与 (1) 中的情形相同,第二组迭代器则只有一个起始位置 first2
而没有尾后截止。这样一来,我们必须保证第二组迭代器对应的容器足够大;即 std::distance(first1, last1) <= std::distance(first2, c2.end())
,其中 c2.end()
表示 first2
对应的容器的尾后迭代器。(2) 与 (1) 还有一处不同在于,(1) 接受一个 UnaryOperation
,而 (2) 接受一个 BinaryOperation
。因此,(2) 通过两个输入迭代器分别获取一个元素,经过 BinaryOperation
处理之后,保存在输出迭代器 d_frist
当中。这与 Python 当中的 map(lambda x, y: return <do_something>, <iterable_1>, <iterable_2>)
类似。
我们用如下代码说明 std::transform
的用法。
1 |
|
这里,(1) 为 vec_out
预留好了足够的空间,避免在后续不断 push_back
的过程中动态扩容,降低效率。在实际工程中,若一个向量的长度是预计确定的,或者能够预估的,那么提前预留好空间能大幅提高效率。
在 (2)(3)(4) 处,我们调用了 std::transform
函数。(2) 处输入了待处理序列的起止位置迭代器(左闭右开);(3) 处输入了结果保存位置的迭代器;(4) 则以 C++ 的 Lambda 函数创建了一个临时的一元函数(求平方)。
在 (5)(6)(7)(8) 处,我们再次调用了 std::transform
函数。(5) 处输入了第一个待处理序列的起止位置迭代器(左闭右开);(6) 处输入了第二个待处理序列的起始位置迭代器(两个 std::vector<int>
长度相同,因而合法);(7) 照例输入了结果保存位置的迭代器;(8) 则以 C++ 的 Lambda 函数创建了一个临时的二元函数(求差)。
这样一来,结果应该是:
1 | $ ./a.out |
std::tolower
和 std::toupper
std::tolower
和 std::toupper
是定义在头文件 cctype
当中的两个函数。它们的函数签名分别是
1 | int tolower(int ch); |
需要额外注意的是,两个函数对参数是有要求的。ch
必须不能是 EOF
,并且必须能转换为 unsigned char
。
对字符串进行大小写转换
考虑到 std::string
和 std::vector
类似,都可以用迭代器进行逐元素地操作;我们可以利用 std::transform
和 std::tolower
及 std::toupper
对整个字符串进行大小写转换。
1 |
|
有了之前的知识,这份代码是不言自明的。它的输出应该是:
1 | $ ./a.out |