Anjana函数式编程演讲小计

评价:Anjana Vakil的讲演方式对于任何有编程基础的工作者来说都是易于理解的,所有人都能从她的演讲中取得收获。

文章中的内容和其演讲内容存在一些不对称,其翻译过后的视频可在这里取得。

何为函数式编程(functional paradigm)

在函数式编程中,你会想用函数来做所有的事情(input -> output),所以我们用函数来表达程序中的所有内容。当然,函数只是一个接收输入并给予输出的东西,所以我们想的是程序输入和输出的数据流的种类,而不是考虑对象及其相互作用,已经它们如何操作等。

我们将使用Javascript来讲述这一主题,请看如下的编程范例。

1
2
3
4
5
// 非函数式
var name = "Anjana";
var greeting = "Hi, I'm ";
console.log(greeting + name);
// => "Hi, I'm Anjana"

如上的代码我们将它称之为命令式风格,即为从上到下依次执行,先做这,后做那,再做其他的。在这里面没有任何函数出现,所以我们不是在以“如何输入会转换为输出”来表达。此时,函数式编程也许是更高效的方法。

1
2
3
4
5
6
function greet(name) {
return "Hi, I'm " + name;
}

greet("Anjana");
// => "Hi, I'm Anjana"

定义一个名为greet的函数,带有参数name,并返回一个字符串,用函数来处理所有的需要,这就是函数式编程。

纯函数和非纯函数

在步入纯函数和非纯函数这个主题之前,我们先来看看纯函数的定义。(以下内容摘自「维基百科」)

  • 此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。

  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。

    纯函数的输出可以不用和所有的输入值有关,甚至可以和所有的输入值都无关。但纯函数的输出不能和输入值以外的任何资讯有关。纯函数可以传回多个输出值,但上述的原则需针对所有输出值都要成立。若引数是传引用调用,若有对参数物件的更改,就会影响函数以外物件的内容,因此就不是纯函数。

我们可以用“函数的副作用(side effects)”去更简单的理解。

函数副作用指当调用函数时,除了返回值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数之外的变量)或修改参数。

纯函数(pure function)

纯函数输入/输出的数据流都是显式(Explicit)的。显式的意思是,函数与外界交换数据只有一个唯一渠道,即为参数和返回值。函数从函数外部接受的所有输入信息都通过参数传递到函数内部,函数内部处理完成的信息通过返回值传递到函数外部。如下编程范例。

1
2
3
function greet(name) {
return "Hi, I'm " + name;
}

这段代码所注重的是函数的参数输入和返回输出,依照准则这个函数是纯函数。

非纯函数(impure function)

如果函数通过隐式(implicit)方式从函数本身外部获取数据,或向外部输出数据,那么该函数就式非纯函数。隐式的意思是函数通过参数和返回值以外的渠道和外界数据进行交换。例如读取和修改全局变量、利用I/O API(输入输出系统函数库)读取配置文件、输出内容到文件、打印文字到屏幕。如下我们也有一个编程范例去解释非纯函数。

1
2
3
4
var name = "Anjana";
function greet() {
console.log("Hi, I'm " + name);
}

这个函数没有参数和返回值,并且它从函数外界读取了全局变量name,按照如上的标准,这显而易见是一个非纯函数。

高阶函数(higher-order functions)

Javascript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能够接收变量,那么一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。下面给出一个高阶函数范例来解释高阶函数。

1
2
3
4
5
6
7
8
9
function makeAdjectifier(adjective) {
return function (string) {
return adjective + " " + string;
};
}

var coolifier = makeAdjectifier("cool");
coolifier("conference");
// => "cool conference"

从makeAdjectifier函数讲起,该函数会返回一个函数。也就是说,当这个函数被执行时,接收返回值的变量就会成为一个函数,内容变换请看下面的范例。

当makeAdjectifier被执行后的coolifier将会呈如下状态。

1
2
3
function coolifier(string) {
return "cool" + " " + string;
}

其中的内容不难理解,由此可以轻松总结出高阶函数的使用方式。当然,高阶函数并不只限制与上述案例,您大可以返回多个函数,但那样是不明智的。

柯里化(Curry)

柯里化是一种关于函数的高阶技术。它不仅被用于Javascript,还被用于其他编程语言。柯里化是指将一个函数从可调用的f(a, b, c)转换为可调用的的f(a)(b)(c)。柯里化不会调用函数,它只是对函数进行转换。请看下面的编程范例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
};
};
}

function sum(a, b) {
return a + b;
}

let curriedSum = curry(sum);

console.log(curriedSum(1)(2));
// => 3

在开头我们创建了一个辅助函数curry,该函数将对具有两个参数的函数f执行柯里化。换句话说,对于两个参数的函数f(a, b)执行curry函数都会将转换为以f(a)(b)形式运行的函数。在此只讲其粗略,更多信息请参考演讲视频和网络。