C++11 函数式编程
C++11 也支持了好多新特性,表达力增强了许多。其实我看主要是人家别的语言都原生或更新支持了,再不支持就太不好意思了,再加上最近 Java 更新速度都快赶上 Chrome 了,7、8、9接踵而至。相较好像 C++ 居委会好像天天无所事事似的,幸好 C++ 是以年份编号,03 之后就是 11 了 :)
lambda
不得不说 lamdba 实在是把利器, 从 《黑客与画家》 上盗个题目,高阶函数加法:
add(a)(b)
function<int(int)> add(int a)
{
return [a](int b){ return a + b; };
}
int main()
{
cout << add(3)(4) << endl;
return 0;
}
这代码好像比其他语言的要长点~,索性改成纯 lambda 的
auto add = [](int a)->function<int(int)> {
return [a](int b) {
return a + b;
};
};
cout << add(3)(4) << endl;
在 Visual Studio 2013 里返回值声明 ->function<int(int)> 不能省略,傻傻的 VS 编译器表示不能自动推导返回值类型。
而试了 g++ 4.82 就不用写明这个返回类型。
这里我们也看到了 auto 的好处,再也不用写乱七八糟好长好恶心的声明,直接 auto 了事,比如这里:
auto add = [](int a)->function<int(int)>{
return [a](int b){
return a + b;
};
};
vector<function<int(int)>> adds(10);
for (int i = 0; i < adds.size(); ++i)
adds[i] = add(i);
for (auto _add : adds)
cout << _add(1) << endl;
上面几个例子有点像是奇技淫巧,感觉实际中用处不大的样子。还是来个实际的,大家会遇到的:排序。
struct Student
{
string name;
int age;
Student(string name, int age) : name(name), age(age){}
};
int main()
{
vector<Student*> students;
students.push_back(new Student("aaa", 12));
students.push_back(new Student("bbb", 10));
sort(students.begin(), students.end(), [](Student *s1, Student *s2){
return s1->age < s2->age;
});
for (auto s : students)
cout << s->name << " " << s->age << endl;
return 0;
}
这么小的函数,单独再写一个实在麻烦,而 lambda 函数就小的刚刚好,如果输入也能自动推断就更好了,直接就写 [](auto s1, auto s2)
函数式编程
Python 中函数式编程核心的三个方法是 map reduce filter,而 Ruby 中是 map inject select。大概的表达的意思都是映射、规约和筛选。 在 C++ 中,我找到了几个类似函数,结合 lambda 写起来杠杠的~
transform
accumulate
for_each
Map
将一组数据统一进行某一操作, 如下求平方
vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
transform(nums.begin(), nums.end(), nums.begin(), [](int n){
return n*n;
});
for (auto n : nums)
cout << n << " ";
Reduce
这个看着比较靠谱的就是 accumulate
auto sum = accumulate(students.begin(), students.end(), 0, [](int acc, Student* s){
return acc + s->age;
});
// 别被名字骗啦,不止是累加~
// 据说这种 begin / end 的写法是新推荐的,统一数组和 vector
// vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int nums[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto muti = accumulate(begin(nums), end(nums), 1, [](int i, int j){
return i * j;
});
Filter
选择元素
找了半天没有找到对应 filter 的,所幸有 for_each 可以自己写
vector<int> result;
for_each(nums.begin(), nums.end(), [&result](int n){
if (n % 3 == 1)
result.push_back(n);
});
for (auto n : result)
cout << n << " ";
其实吧这些都是可以用 for_each 或者 for(auto s : students) 改写的。而且看着也都并不麻烦。
不过要吐槽的一点就是写着越来越像 js 了。
One More Thing
还有一些函数也是经常配合使用的。
iota
all_of
any_of
copy_if
count_if
remove_if
...
PS
还有个不错的东东也挺不错的,元组 tuple。
看到元组的时候,首先想到的应用是函数返回多个值。
比如返回最大值及坐标,还能通过 idx == -1 判断是否是有效值。
tuple<int, int> max(vector<int> &vec)
{
int value=0, idx=-1;
for ( auto it=begin(vec); it!=end(vec); ++it)
{
if ( idx=-1 || *it>value )
tie(value, idx) = make_tuple(*it, it-begin(vec));
}
return make_tuple(value, idx);
}
// main()
int value, idx;
// tie(value, idx) = max(nums);
tie(value, ignore) = max(nums);
auto max_idx = max(nums);
get<0>(max_idx); // value
get<1>(max_idx); // idx
然后想到的就是鸭子类型,tuple 作为能容纳不同类型的容器,如果加上容器遍历,这样就能调用具有相同方法的对象。
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
很可惜,我没有找到这样的遍历方法,有用函数模板写的遍历方式,不够通用普遍。再想想要实现这个,貌似还需要动态类型和反射,估计很难办到~
不过总体来说 tuple 还是很有意思的~