跟我学c++初级篇——transform用法

224 篇文章 92 订阅

一、介绍

transform,改变,这个改变是内容的改变。那么在STL标准库里提供的这个函数,也是这个意思。在实际的应用场景中,开发者经常遇到一些这样的场景:两个数组按照顺序进行一个算法操作,最典型的就是把两个数组中的值相加然后形成一个新数组。传统的方式怎么解决呢?一般来说,做一个循环动作,然后分别取出两个数组中的对应元素进行加的动作,然后把结果存储到一个新的数组中去。这没有什么问题,只是代码看起来有很多重复的东西,意义不大。
类似的操作在实际开发还有,不一定非是数组。所以在c++的标准库中提供了std::transform这个函数,用来实现这个动作,抽象出一层来进行某个方向上的普适。

二、c++库提供的std::transform

在c++的标准库的提供了std::transform,其定义如下:

<algorithm> 定义
(1)	
template< class InputIt, class OutputIt, class UnaryOperation >
OutputIt transform( InputIt first1, InputIt last1,
                    OutputIt d_first, UnaryOperation unary_op );(C++20 前)
template< class InputIt, class OutputIt, class UnaryOperation >
constexpr OutputIt transform( InputIt first1, InputIt last1,
                              OutputIt d_first, UnaryOperation unary_op );(C++20 起)
(2)
template< class ExecutionPolicy, class ForwardIt1,
          class ForwardIt2, class UnaryOperation >
ForwardIt2 transform( ExecutionPolicy&& policy,
                      ForwardIt1 first1, ForwardIt1 last1,
                      ForwardIt2 d_first, UnaryOperation unary_op );(C++17 起)
(3)	
template< class InputIt1, class InputIt2,
          class OutputIt, class BinaryOperation >
OutputIt transform( InputIt1 first1, InputIt1 last1, InputIt2 first2,
                    OutputIt d_first, BinaryOperation binary_op );(C++20 前)
template< class InputIt1, class InputIt2,
          class OutputIt, class BinaryOperation >
constexpr OutputIt transform( InputIt1 first1, InputIt1 last1, InputIt2 first2,
                              OutputIt d_first, BinaryOperation binary_op );(C++20 起)
(4)
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2,
          class ForwardIt3, class BinaryOperation >
ForwardIt3 transform( ExecutionPolicy&& policy,
                      ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
                      ForwardIt3 d_first, BinaryOperation binary_op );(C++17 起)
std::transform applies the given function to a range and stores the result in another range, keeping the original elements order and beginning at d_first.

1) The unary operation unary_op is applied to the range defined by [first1, last1).
3) The binary operation binary_op is applied to pairs of elements from two ranges: one defined by [first1, last1) and the other beginning at first2.
2,4) Same as (1,3), but executed according to policy. These overloads do not participate in overload resolution unless
std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> is true.

(until C++20)
std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true.

(since C++20)
unary_op and binary_op must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved.

Parameters
first1, last1-	the first range of elements to transform
first2	-	the beginning of the second range of elements to transform
d_first	-	the beginning of the destination range, may be equal to first1 or first2
policy	-	the execution policy to use. See execution policy for details.
unary_op	-	unary operation function object that will be applied.
The signature of the function should be equivalent to the following:

 Ret fun(const Type &a);

The signature does not need to have const &.
The type Type must be such that an object of type InputIt can be dereferenced and then implicitly converted to Type. The type Ret must be such that an object of type OutputIt can be dereferenced and assigned a value of type Ret.​

binary_op	-	binary operation function object that will be applied.
The signature of the function should be equivalent to the following:

 Ret fun(const Type1 &a, const Type2 &b);

The signature does not need to have const &.
The types Type1 and Type2 must be such that objects of types InputIt1 and InputIt2 can be dereferenced and then implicitly converted to Type1 and Type2 respectively. The type Ret must be such that an object of type OutputIt can be dereferenced and assigned a value of type Ret.​

Type requirements
-InputIt, InputIt1, InputIt2 must meet the requirements of LegacyInputIterator.
-OutputIt must meet the requirements of LegacyOutputIterator.
-ForwardIt1, ForwardIt2, ForwardIt3 must meet the requirements of LegacyForwardIterator.
Return value
Output iterator to the element past the last element transformed.

Complexity
1-2) Exactly std::distance(first1, last1) applications of unary_op
3-4) Exactly std::distance(first1, last1) applications of binary_op
Exceptions
The overloads with a template parameter named ExecutionPolicy report errors as follows:

If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.
If the algorithm fails to allocate memory, std::bad_alloc is thrown.

看上面的说明其实很清晰,只要注意一些特别的点就好,比如适用范围等。其实现的原理和自己写代码实现的原理基本类似,只是可能底层库更兼顾了一些平衡,比如平台的兼容性等,这样会使一些固定的技巧失效。而此函数的操作也是模板化的地址增加按算法操作后向目的地址赋值。

三、例程

先看一个标准库提供的例程:

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
 
int main()
{
    std::string s{"hello"};
    std::transform(s.cbegin(), s.cend(),
                   s.begin(), // 写入相同位置
                   [](unsigned char c) { return std::toupper(c); });
    std::cout << "s = " << quoted(s) << '\n';//quoted这个函数是给字符串加双引号的
 
    // 使用 std::for_each 实现相同效果(见上文注解)
    std::string g{"hello"};
    std::for_each(g.begin(), g.end(), [](char& c) // 当场修改
    {
        c = std::toupper(static_cast<unsigned char>(c));
    });
    std::cout << "g = " << quoted(g) << '\n';
 
    std::vector<std::size_t> ordinals;
    std::transform(s.cbegin(), s.cend(), std::back_inserter(ordinals),
                   [](unsigned char c) { return c; });
 
    std::cout << "序数:";
    for (auto ord : ordinals)
       std::cout << ord << ' ';
 
    std::transform(ordinals.cbegin(), ordinals.cend(), ordinals.cbegin(),
                   ordinals.begin(), std::plus<>{});
 
    std::cout << "\n序数:";
    for (auto ord : ordinals)
       std::cout << ord << ' ';
    std::cout << '\n';
}

运行结果:

s = "HELLO"
g = "HELLO"
序数:72 69 76 76 79
序数:144 138 152 152 158

再看一个和其它算法操作结合的:

#include <iostream>  
#include <functional>  
#include <algorithm>  
  
int main() 
{ 
    //data source
    int src0[] = { 11, 22, 33, 44, 55 }; 
    int src1[] = { 9, 8, 7, 6, 5 }; 
  
   //output arr
    int out[5]; 
  
    std::transform(src0, src0 + 5, src1,out, std::plus<int>()); 
  
    for (int i = 0; i < 5; i++) 
        std::cout << out[i] << " "<<std::endl; 
  
    return 0; 
}

运行结果:

20 30 40 50 60

其实在c++的标准库里还有不少类似的函数,比如处理累加的std::accumulate还有求最小最大的等,正如反复重复的,没有最好只有最合适,一些能省的代码还是省一下,不过不原意省,也无所谓。

四、总结

程序员最大乐趣其实就是造轮子。这就和每个小孩子都原意把一个玩具拆了装,装了拆,道理是一样的。一开始追求不是结果,是创造的过程;而后来,越发注重结果情况的就是更加强化创造过程中的技术和技巧。等过了以创造轮子为乐趣的时光,就不原意创造轮子了。可是,社会又逼迫你造轮子,于是很多程序员又很痛苦。人生也是如此,你想做某件事的时候儿,人们往往批评你;而当你不想做了,还是批评你。不是人们不讲道理,是时间一去不复返了。
每一个时代,有这个时代人的责任;每一段人生的时光,有每一个人表演的舞台。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值