c++实现类似python的map一样,批量操作一个vector的功能【python一样写c++、三】

python里有一个东西,叫map。
它可以实现像这样,对list每个元素进行操作,并返回新的list(python3是迭代器)
像这样

a=list(map(int,input().split()))
b=list(map(lambda x:x*2,a))

遇到这个,我们就会像:c++可不可以也搞一个,对vector每个元素进行操作的函数?

emm,就叫my_map好了。

有了想法,那就开干呗!

老样子,先放成品(完整程序和注释版在后面)

template<typename Ori,typename Func_Type>
auto my_map(Func_Type my_func,vector<Ori> &x){
	using return_type = typename result_of<Func_Type(Ori)>::type;
	vector<return_type> tmp;
    for(auto i :x)
        tmp.push_back(my_func(i));
    return tmp;
}

int main()
{	
    auto s=input().split();
    cout<<s;
	
    auto a=my_map(my_int,s);//my_int是函数,string转int
    cout<<a;
    
    auto b=my_map(Dao_Shu(),a);//Dao_Shu是一个重载了()的类(结构体)
    cout<<b;
    
    auto ping_fang=[](int x) { return x*x;};
    auto c=my_map(ping_fang,a);//ping_fang是lambda表达式(平方的作用)
    cout<<c;
    
    long long sum=0;//同理,可以实现这种操作:求前缀和
    auto leijia=[=](int x) mutable{ return sum+=x;};
    auto d=my_map(leijia,a);
    cout<<d;  
}

效果

本文作者XXOY

那个input().split(),和cout<<vector可以看我之前的文章
input().split()

cout<<

这次的代码虽然短,但或许确实是(目前)技术含量最高的一篇了。

0.前置芝士:
(1):c++11的新特性(比如auto、lambda)
(2):c++的template(模板)与函数指针

1.首先考虑要实现的功能:
传一个函数与一个vector,返回对vector中每一个元素使用这个函数的新vector。

于是就有了第一版

template<typename T,typename T2>
vector<T2> my_map(T2 (*co)(T),vector<T> yuan){
    vector<T2> x;
    for(auto i:yuan)
        x.push_back(co(i));
    return x;
}

传递一个函数指针进去,然后进行操作。

但这样只能搞普通的函数,不能操纵lambda,会报错。

进行了一番百度、google以后发现lambda本质是一个重载过()的类(某种意义上)

于是有了第二版:

template<typename Ori,typename Func_Type>
auto my_map(Func_Type my_func,vector<Ori> &x){
	auto k=my_func(yuan[0]);
	vector<decltype(k)> tmp;
    for(auto i :x)
        tmp.push_back(my_func(i));
    return tmp;
}

为什么搞一个k呢?因为那时我不知道如何获取一个函数返回值的类型,只能傻傻的先去操作一下先。

会遇到什么问题呢?空的vector会报错。

遂继续百度(这足足花了两天)。

才知道有种东西叫result_of,专门用来获取返回值类型的。

于是--->第三版

template<typename Ori,typename Func_Type>
auto my_map(Func_Type my_func,vector<Ori> &x){
	using return_type = typename result_of<Func_Type(Ori)>::type;
	vector<return_type> tmp;
    for(auto i :x)
        tmp.push_back(my_func(i));
    return tmp;
}
//result_of 是获取返回值类型的意思。
//可以不需要T2 (*my_func)(T)像这样定义函数,直接把它当一个类用就行了
//对于lambda:某种意义上,它本质上就是一个重载了()的类

OK,写完,收工。

现在,它可以实现如下骚操作。

    auto s=input().split();
	//s的类型是vector<string>
	cout<<s;
	
    auto a=my_map(my_int,s);
	//my_int是函数,string转int
	//a的类型是vector<int>
    cout<<a;
    
    //Dao_Shu是一个重载了()的类(结构体)
    auto b=my_map(Dao_Shu(),a);
    //DS是一个类型为Dao_Shu的变量
    //也可以auto b=my_map(DS,a);
    //b是vector<double>
    cout<<b;
    
    auto ping_fang=[](int x) { return x*x;};
    //ping_fang是lambda表达式(平方的作用)
    auto c=my_map(ping_fang,a);
	//c是vector<int>
    cout<<c;
    
    long long sum=0;
    auto leijia=[=](int x) mutable{ return sum+=x;};
    //同理,可以实现这种操作:求前缀和
    auto d=my_map(leijia,a);
    //d是vector<long long>
    cout<<d;

完整代码:

#include<bits/stdc++.h>
using namespace std;

//split()和input()的实现
struct mystring:string{//继承string类 
    mystring() :string() {}
    mystring& operator=(const string &c) { 
        (*this).assign(c);
        return *this;
    }
    vector<string> split(char c=' '){
        vector<string> x;
        int last=0,len=0;
        for(int i=0;i<size();++i)  
			if(at(i)==c){
                x.push_back(substr(last,len));
                last=i+1;
                len=0;
            }
            else  ++len;
        if(last<size())
            x.push_back(substr(last,len));
        return x;
    }
}s;

mystring input(){
	mystring s;
	getline(cin,s);
	return s;
}

//重载cout<<
namespace py_pr{
	template<typename T>
	inline ostream& out_put(ostream& o,const T & x){
		return o<<x;
	}
	inline ostream& out_put(ostream& o,const string& x){
		return o<<"\""<<x<<"\"";
	}		
	inline ostream& out_put(ostream& o,const char* & x){
		return o<<"\""<<x<<"\"";
	}			
	inline ostream& out_put(ostream& o,const char & x){
		return o<<"\'"<<x<<"\'";
	}
	template<typename T1,typename T2>
	inline ostream& out_put(ostream& o,const pair<T1,T2> & x){
		out_put(o,x.first);
		o<<": ";
		out_put(o,x.second);
		return o;
	}			
	
}

template<typename T>
ostream& operator<<(ostream &o,vector<T> &x){
    o<<"[";
    for(auto i=x.begin();i<x.end();++i){
		//可以直接for(auto i:x),但是我不知道怎么特判第一个来控制","
        if(i!=x.begin()) o<<", ";
       	py_pr::out_put(o,*i); 
    }
    o<<"]"<<endl;
    return o;
}
template<typename Ori,typename Func_Type>
auto my_map(Func_Type my_func,vector<Ori> &x){
	using return_type = typename result_of<Func_Type(Ori)>::type;
	vector<return_type> tmp;
    for(auto i :x)
        tmp.push_back(my_func(i));
    return tmp;
}
//result_of 是获取返回值类型的意思。
//可以不需要T2 (*my_func)(T)像这样定义函数,直接把它当一个类用就行了
//对于lambda:某种意义上,它本质上就是一个重载了()的类


int my_int(const string x){
	return stoi(x);
}

struct Dao_Shu{
	double operator() (double x){
		return 1.0/x;
	}
}DS;

int main()
{
    auto s=input().split();
	//s的类型是vector<string>
	cout<<s;
	
    auto a=my_map(my_int,s);
	//my_int是函数,string转int
	//a的类型是vector<int>
    cout<<a;
    
    //Dao_Shu是一个重载了()的类(结构体)
    auto b=my_map(Dao_Shu(),a);
    //DS是一个类型为Dao_Shu的变量
    //也可以auto b=my_map(DS,a);
    //b是vector<double>
    cout<<b;
    
    auto ping_fang=[](int x) { return x*x;};
    //ping_fang是lambda表达式(平方的作用)
    auto c=my_map(ping_fang,a);
	//c是vector<int>
    cout<<c;
    
    long long sum=0;
    auto leijia=[=](int x) mutable{ return sum+=x;};
    //同理,可以实现这种操作:求前缀和
    auto d=my_map(leijia,a);
    //d是vector<long long>
    cout<<d;
    
}

最后的最后Q&A环节:
1.Q:为什么要搞这种东西呢?
A:因为我开心。

2.不能做到的:直接传stoi之类的函数。
原因大概是其中有缺省参数,参数表不匹配。会编译不通过

解决办法:包个壳(感谢@たくやりん 提供的方法)

比如这样int my_int(string x){ return stoi(x); }

更多内容,包括那个input().split(),和cout<<vector可以看我的主页。
input().split()
cout<<

我是XXOY,我们下次再见。

posted @ 2021-07-15 15:19  箱庭  阅读(244)  评论(0编辑  收藏  举报