返回 设计闭包(Cl...... 登陆

设计闭包(Closure)的初衷

高洛峰 2016-11-01 14:33:09 544

闭包是函数式编程及其核心思想“Lambda 计算法”(Lambda Calculus)的必备基本设定。

我们都知道:

函数式编程有一个特点,就是所有操作都用可计算的函数(computable function,下简称“函数”)来体现。

函数的两个特点,就是每个函数都有一个输入值,一个输出值。

函数还有一个定律,就是给定一个确定的输入值,总能得到一个确定的输出值,即输入输出有严格的一一对应关系。

(当然还有无副作用之类,此处不论。)


那么,在这样的限制条件下,请问,“加法”的函数怎么写?

function plus(senior) {
    return senior + 1;}

是,这倒是 +1s 了,但是怎么加任意数值呢?加法的定义是 a + b,两个参数。记住,函数只能有一个输入,一个输出。

聪明的人们想到了一个办法,如果函数 A 拿到参数 ① 之后能够返回另一个函数 B,而函数 B 拿到参数 ②,再把参数 ① 和 ② 糅合起来就可以了。

function plusAny(senior) {
   return function(second) {
        return senior + second;
   }}/* usage */var senior = 2838240000;var longLiveSeniorFunc = plusAny(senior);longLiveSeniorFunc(1);     // +1slongLiveSeniorFunc(3600);  // +1hlongLiveSeniorFunc(86400); // +1d

(注意输入输出有严格对应关系,所以不管呼叫 longLiveSeniorFunc(1) 多少次结果都是一样。所以,每次 +1 前应该重新定义 longLiveSeniorFunc,使用最新的 senior。此处省略。)

有了此法,不管多少参数,都可以用此法来转换成一个参数。

不过等一下,这需要我们做一些设定:

首先,认可“函数”和“数字”、“字符”等等一样,是一种“值”(value)

然后,认可“函数”可以和其他值一样,可以赋给一个变量,返回,或者当做参数来传递


这意味着函数享有其他“值”的便利,可称之为一个语言的“一等公民”(first-class citizen)。不过,这时候这个函数还只是一个匿名函数(anonymous function),还不能称之为闭包。

而这个 plusAny 函数,就是一个闭包。因为它返回了一个函数,且此函数“包”进了 plusAny 函数作用域里的一个本地变量 senior,并且可以在其存续期间持续使用这个变量。

那么,为什么 A 函数内部定义的函数能访问 A 函数作用域的变量?为什么要这样设定?很简单,因为不这样设定,函数式编程就连加法都没法定义啊……

所以,闭包是函数式的基本配备。

当然,函数入魔的朋友们也搞出了很多滥用的幺蛾子,那又是后话了……


更新
-----

关于函数为什么只能有一个参数的问题,试分析一下。

第一是抽象。既然所有“多参数函数”都可以抽象成一个高级的“单参数函数”(用上面的方法),那么意味着“多参数函数”本身是有信息冗余的。这个抽象的过程就像是拆掉分子分母的公约数一样,去掉了这个冗余。

第二是计算。我们想象一下任何一种“计算机器”,它要做一个最简单的加法,那么它需要:

读取 a,保存;

读取 b,计算 a + b;

返回结果。


那么可以看出,在第 1 步时如果这个机器暂停,那么实际上它保存了一个“状态”(state),而没有进行任何“计算”。

如果把这个保存了某个状态的机器视作是一个新的机器(在内存里写死了 a 值,等待 b 值做加法),那么新的机器才是真正在做“计算”。原来什么值都没有保存的机器有没有在做计算呢?我们不妨认为它也在做“计算”,这个计算的参数则是 a 值,计算结果就是前面说的新机器。

如此,既确保了没有信息冗余(严格说第 2 步还可以继续拆),状态被作为显性的参数所捕捉,并且每一个步骤都是一次“计算”,且计算的结果和参数一一对应。

换句话说,就是函数成了“无状态”,它返回什么,完全且仅取决于你给它喂什么参数。只要你喂的参数不变,到世界末日它也会返回给你同样的结果。这进一步意味着这个函数是完全独立的,可以独立存在(比如抽成库或者服务),可以很容易地替换(只需确保输入输出对应关系),可以很容易地测试(只需检查输入输出)。

那么,为什么要这么严格而极致地来定义这样一套体系?因为先行者们想发明一套通用的、可计算任意算式的计算机架构,比如与 Lambda Calculus 同时期的图灵机。

了解了这些,就能领略函数式编程之美,并且可以用更批判的眼光看待各种编程语言,还会对架构设计,测试编写等大小方面有各种好处。


最新手记推荐

• 用composer安装thinkphp框架的步骤 • 省市区接口说明 • 用thinkphp,后台新增栏目 • 管理员添加编辑删除 • 管理员添加编辑删除

全部回复(0)我要回复

暂无评论~
  • 取消 回复 发送
  • PHP中文网