【转】JavaScript函数柯里化的一些思考

原文地址:https://www.cnblogs.com/pengchen/p/5434705.html

1. 高阶函数的坑

在学习柯里化之前,我们首先来看下面一段代码:

很多同学都能看出来,这些写是非常傻的,因为函数 f1f是等效的,我们直接令 var f1 = f;就行了,完全没有必要包裹那么一层。

但是,下面一段代码就未必能够看得出问题来了:

是等价的,所以函数可以化简为:

继续化简:

如此一来,我们发现那么长一段程序都白写了。
函数既可以当参数,又可以当返回值,是高阶函数的一个重要特性,但是稍不留神就容易踩到坑里。

2. 函数柯里化(curry)

言归正传,什么是函数柯里化?函数柯里化(curry)就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。听得很绕口,其实很简单,其实就是将函数的变量拆分开来调用: f(x,y,z) -> f(x)(y)(z)

对于最开始的例子,按照如下实现,要传入两个参数, f1调用方式是 f1(f,x)

注意,由于 f是作为一个函数变量传入,所以 f1变成了一个新的函数。

我们将 f1变化一下,利用闭包可以写成如下形式,则 f1调用方式变成了 f1(f)(x),而且得到的结果完全一样。这就完成了 f1的柯里化。

其实这个例子举得不恰当,细心的同学可能会发现, f1虽然是一个新函数,但是 f2f是完全等效的,绕了半天,还是绕回来了。

这里有一个很经典的例子:

由于 parseInt接受两个参数,所以直接调用会有进制转换的问题,参考“不愿相离”的文章。
var f2 = f1(parseInt)f2parseInt由原来的接受两个参数变成了只接受一个参数的新函数,从而解决这个进制转换问题。通过我们的 f1包裹以后就能够运行出正确的结果了。

3. 函数柯里化进一步思考

如果说上一节的例子中,我们不是直接运行 f(x),而是把函数 f当做一个参数,结果会怎样呢?我们来看下面这个例子:
假设 f1返回函数 gg的作用域指向 xs,函数 f作为 g的参数。最终我们可以写成如下形式:

反curring就是把原来已经固定的参数或者this上下文等当作参数延迟到未来传递。
它能够在很大程度上简化函数,前提是你得习惯它。

抛开反柯里化,如果我们要柯里化 f1怎么办?
使用闭包,我们可以写成如下形式:

f传入 f1中,我们就可以得到 f2这个新函数。

只传给函数一部分参数通常也叫做局部调用(partial application),能够大量减少样板文件代码(boilerplate code)。

当然,函数 f1传入的两个参数不一定非得包含函数+非函数,可能两个都是函数,也可能两个都是非函数。

我个人觉得柯里化并非是必须的,而且不熟悉的同学阅读起来可能会遇到麻烦,但是它能帮助我们理解JS中的函数式编程,更重要的是,我们以后在阅读类似的代码时,不会感到陌生。知乎上罗宸同学讲的挺好:

并非”柯里化”对函数式编程有意义。而是,函数式编程在把函数当作一等公民的同时,就不可避免的会产生”柯里化”这种用法。所以它并不是因为”有什么意义”才出现的。当然既然存在了,我们自然可以探讨一下怎么利用这种现象。

分析:函数 filterQs的作用是:传入一个字符串数组,过滤出包含’q’的字符串,并组成一个新的数组返回。
我们可以通过如下步骤得到函数 filterQs

a. filter传入的两个参数,第一个是回调函数,第二个是数组, filter主要功能是根据回调函数过滤数组。我们首先将 filter函数柯里化:

b. 其次, filter函数传入的回调函数是 matchmatch的主要功能是判断每个字符串是否匹配 what这个正则表达式。这里我们将 match也柯里化:

创建匹配函数 match2,检查字符串中是否包含字母q。
c. 把 match2传入 filter中,组合在一起,就形成了一个新的函数:

从这个示例中我们也可以体会到函数柯里化的强大。所以,柯里化还有一个重要的功能:封装不同功能的函数,利用已有的函数组成新的函数。

4. 函数柯里化的递归调用

add函数返回闭包 retVal,在 retVal中又继续调用 add,最终我们可以写成 add(1)(2)(3)(...)这样柯里化的形式。
关于这段代码的解答,知乎上的李宏训同学回答地很好:

每调用一次add函数,都会返回retValue函数;调用retValue函数会调用add函数,然后还是返回retValue函数,所以调用add的结果一定是返回一个retValue函数。add函数的存在意义只是为了提供闭包,这个类似的递归调用每次调用add都会生成一个新的闭包。

5. 函数组合(compose)

函数组合是在柯里化基础上完成的:

将传入的函数变成两个,通过组合的方式返回一个新的函数,让代码从右向左运行,而不是从内向外运行。

函数组合和柯里化有一个好处就是pointfree。

pointfree 模式指的是,永远不必说出你的数据。它的意思是说,函数无须提及将要操作的数据是什么样的。一等公民的函数、柯里化(curry)以及组合协作起来非常有助于实现这种模式。

Original: https://www.cnblogs.com/PeunZhang/p/12569125.html
Author: 白树
Title: 【转】JavaScript函数柯里化的一些思考

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/535182/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球