函数式思维(二)-- 为何你想不到用 reduce

栏目: JavaScript · 发布时间: 5年前

内容简介:上次我写了一篇简单介绍函数式思维的文章,我们组的同学看了之后表示很感兴趣,希望我有空多写写这方面的内容,然后表示他能想到用数组的 map,但是想不到 reduce。我想这可能也是个普遍现象,因为在对 FP(函数式编程)接触不多的同学来讲,脑海中对 map 的印象,可能基本等同于循环,而对 reduce 就相对陌生。但其实呢,reduce 是个比 map、flatMap 啥的更通用的函数,你可以用 reduce 轻易地实现其他函数。我们先实现一下 reduce:JS 里

上次我写了一篇简单介绍函数式思维的文章,我们组的同学看了之后表示很感兴趣,希望我有空多写写这方面的内容,然后表示他能想到用数组的 map,但是想不到 reduce。我想这可能也是个普遍现象,因为在对 FP(函数式编程)接触不多的同学来讲,脑海中对 map 的印象,可能基本等同于循环,而对 reduce 就相对陌生。但其实呢,reduce 是个比 map、flatMap 啥的更通用的函数,你可以用 reduce 轻易地实现其他函数。

我们先实现一下 reduce:

// foldl
const reduce = (reducer, acc) => arr => {
    // reduceRight: const x = arr.pop();
    const x = arr.shift();
    if (!x) return acc;

    return reduce(reducer, reducer(acc, x))(arr);
};
复制代码

JS 里 Array.prototype.reduce 跟我这个稍有不同,它的 reducer 可以接收四个参数(比我的版本多了 currentIndex 和 array),某种程度上我的实现更 FP 一点,有 currentIndex 这个参数,基本就告诉我们它的实现是通过循环做的,而且这多出来的两个参数在什么场景下会真的必须用到呢?我想是在你 reduce 的过程中还在改变原数组的情况下,所以需要从参数拿到更新后的 array(也就意味着产生了副作用),这本身就很不 FP,不可变性和无副作用是 FP 的重要特性,就算我们在真实开发中无法完全做到,但既然是在谈 reduce,那总该尽量避免。所以个人感觉 JS 的这个 reduce 从 FP 的角度看的话有点不伦不类。

然后再解释下为啥我的 reduce 不是直接接收三个参数,而要用部分应用的形式,先接收两个,返回一个接收一个参数的函数呢?是为了复用,我们看个例子,用 reduce 实现一个 sum 函数,把数组里的元素都累加起来:

const arr = [1, 2, 3]; // 下文所有的 arr 都是这个

const sum = reduce((acc, x) => acc + x, 0);
sum(arr); // => 6
复制代码

reduce 在接收不同的 reducer 和 acc 的时候会返回不同的函数,这里是返回 sum,是不是就很顺。而如果 reduce 是一个接收三个参数的函数,那 sum 就得是 const sum = (arr) => reduce((acc, x) => acc + x, 0, arr) ,也不是不可以,但比较丑陋。

接下来我们用 reduce 实现数组的其他方法:length、map、flatMap、includes、find

// JS 的 Array.length 跟我这个实现不一样,
// arr[100] = 1,arr.length 就为 101 了,因为 JS 的 Array 本质是对象
const length = reduce(acc => acc + 1, 0);
length(arr); // => 3

const map = func => reduce((acc, x) => [...acc, func(x)], []);
map(x => x + 1)(arr); // => [2, 3, 4]

const flatMap = func => reduce((acc, x) => [...acc, ...func(x)], []);
flatMap(x => [x + 1])(arr); // => [2, 3, 4]

const includes = element => reduce((acc, x) => acc || (x === element), false);
includes(1)(arr); // => true

// 找到第一个符合条件的元素返回,否则返回 undefined
const find = func => reduce((acc, x) => acc || (func(x) ? x : undefined), undefined);
find(x => x > 2)(arr); // => 3
复制代码

我们有时候处理字符串,也会想用到 reduce、map 啥的,那我们就给 String.prototype 加上:

// string
String.prototype.reduce = function (reducer, acc) {
    return reduce(reducer, acc)(this.split(''));
};

String.prototype.map = function (func) {
    return map(func)(this.split('')).join('');
};

const str = '123';
str.reduce((acc, x) => acc + Number(x), 0); // => 6
str.map(x => Number(x) + 1); // => '234'
复制代码

emmmmmmm……就这样吧。


以上所述就是小编给大家介绍的《函数式思维(二)-- 为何你想不到用 reduce》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Rust

Programming Rust

Jim Blandy / O'Reilly Media / 2016-8-25 / GBP 47.99

This practical book introduces systems programmers to Rust, the new and cutting-edge language that’s still in the experimental/lab stage. You’ll learn how Rust offers the rare and valuable combination......一起来看看 《Programming Rust》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换