什么是Map/Reduce
维基百科中的解释:
MapReduce is a programming model and an associated implementation for processing and generating big data sets with a parallel, distributed algorithm on a cluster.
它是一种编程模型和相关的实现,用于在集群上使用并行分布式算法来处理和生成大数据集。
map
将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
reduce
把一个函数作用在一个序列上, 并把结果继续和序列的下一个元素做累积计算。
Python3中的Map/Reduce
Map
1 2 3 4
| l = [2, 3, 4] l = list(map(lambda x: x * 2, l)) print(l)
|
将列表中的每个元素都乘以2.
Map接受两个参数。
第一个参数是一个函数。这个函数接受一个参数,它就是迭代的元素。
第二个参数是被迭代的序列。
注意:在Python2中返回的是list, 而在Python3中返回的是一个map object
.
Reduce
1 2 3 4 5
| import functools l = [2, 3, 4] sum = functools.reduce(lambda prev, curr: prev + curr * 2, l, 0) print(sum)
|
将列表中的每个元素乘以2再累加求和。
再来一个稍微更复杂的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import functools
def reduce_func(prev, curr): prev[curr["name"]] = curr["age"] return prev
l = [ { "name": "A", "age": 12 }, { "name": "B", "age": 20 }, { "name": "C", "age": 16 }, { "name": "D", "age": 30 }, { "name": "E", "age": 56 }, ]
result = functools.reduce(reduce_func, l, {}) print(result)
|
Reduce接受三个参数。
第一个参数是一个函数。这个函数接受两个参数,第一个是上一个累加的值,第二个是当前迭代的元素。
第二个参数是被迭代的序列。
第三个参数是初始累加值。
JavaScript中的Map/Reduce
用法和Python中的大同小异。
Map
1 2 3 4
| let l = [2, 3, 4] l = l.map( x => x * 2) console.log(l)
|
更详细的介绍参考MDN.
Reduce
1 2 3 4
| let l = [2, 3, 4] result = l.reduce((prev, curr) => prev + curr, 0) console.log(result)
|
1 2 3 4 5 6 7 8 9 10 11
| let l = [ { "name": "A", "age": 12 }, { "name": "B", "age": 20 }, { "name": "C", "age": 16 }, { "name": "D", "age": 30 }, { "name": "E", "age": 56 }, ];
let result = l.reduce( (prev, curr) => { prev[curr.name] = curr.age; return prev;}, {} ); console.log(result);
|
更详细的介绍参考MDN.
注意:若JS解析引擎使用的是es6之前的标准,可用lodash替代。
C++中的Map/Reduce
Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <vector> #include <algorithm>
int main(int argc, char *argv[]) { std::vector<int> v{2, 3, 4}; std::transform(v.begin(), v.end(), v.begin(), [](int i) { return i * 2; }); std::for_each(v.begin(), v.end(), [](int i) { std::cout << i << std::endl; }); return 0; }
|
上面这种方式会直接修改原序列。
如果我们不期望修改原序列,应该这样 :
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <iostream> #include <vector> #include <algorithm> #include <iterator>
int main(int argc, char *argv[]) { std::vector<int> v{2, 3, 4}; std::vector<int> ordinals; std::transform(v.begin(), v.end(), std::back_inserter(ordinals), [](int i) { return i * 2; }); std::for_each(ordinals.begin(), ordinals.end(), [](int i) { std::cout << i << std::endl; }); return 0; }
|
Reduce
直到C++17标准,才加入了std::reduce
.
一个简单的替代实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <vector> #include <algorithm> #include <iterator> #include <functional>
template <typename T> T reduce(const std::vector<T>& data, const std::function<T(T,T)>& reduceFn) { typedef typename std::vector<T>::const_iterator Iterator; Iterator it = data.cbegin(); Iterator end = data.cend(); if (it == end) { throw 0; } else { T accumulator = *it; ++it; for (; it != end; ++it) { accumulator = reduceFn(accumulator, *it); } return accumulator; } }
int main(int argc, char *argv[]) { std::vector<int> v{2, 3, 4}; int result = reduce<int>(v, [](int prev, int curr){ return prev + curr; }); std::cout << result << std::endl; return 0; }
|
Reference