简述
如果你没接触过函数式编程,那么希望这篇文章能帮助到你。如果你也恰好使用的是javascript,那么就再好不过啦~
-
本文首先介绍函数式编程的定义及其核心的概念:λ演算;
-
接下来结合函数式编程的特点来帮助理解λ演算,发现其中一以贯之的原理;
-
再结合函数式编程常用的工具,加深对上面所述的数式编程的特点的理解;
-
最后用两个在平常就可能接触过的例子来打破函数式编程的壁垒,望君在本文大量文字的背后能把握最核心的东西。
1.定义
函数式编程(英语:functional programming)或称函数程序设计、泛函编程,是一种编程范式,它将计算机运算视为函数运算,并且避免使用程序状态以及易变对象。其中,λ演算(lambda calculus)为该语言最重要的基础。而且,λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。
----维基百科
所以,接下来我们来了解下什么是λ演算。
λ演算
λ运算的核心是λ表达式,以此形成函数定义、函数应用和递归的形式系统。当使用λ表达式定义出布尔值、数值和各种基本操作符等语言元素后,就能够形成一种编程语言,所以,λ运算是函数式编程语言共同的祖先.
由于任何一个可计算函数都能用λ运算来表达和求值,因而它等价于图灵机(图灵完备)。
λ表达式(也可以称之为项:λ-expression or λ-term)有且仅有以下三种合法形式:
1.变量(Variable)
形式 x
描述:变量名可能是一个字符或字符串,它表示一个参数(形参)或者一个值(实参)。
e.g.: bar
x
y
z
2.抽象(Abstraction)
形式: λx.M
描述:它表示获取一个参数x
并返回M
的lambda函数,M
是一个合法lambda表达式,且符号λ和.表示绑定变量x于该函数抽象的函数体M。简单来说就是表示一个形参为x的函数M。
e.g.: λx.y
λx.(λy.xy)
,其中:
λx.y
表示一个常量函数(constant function),输出恒为y与输入无关
λx.(λy.xy)
的输出是一个函数抽象λy.xy
,输入可以是任意的lambda表达式。
注意:一个lambda函数的输入和输出也可以是函数。
3.应用(Application)
形式:M N
描述:它表示将函数M
应用于参数N
,其中M
、N
均为合法lambda表达式。简单来说就是给函数M
输入实参N
。
e.g.: (λx.x) y
, (λx.x) (λx.x)
,其中:
(λx.x) y
表示将函数λx.x
应用于变量y
,得到y
;
(λx.x) (λx.x)
表示将函数λx.x
应用于λx.x
,得到λx.x
。函数λx.x
是一个恒等函数(identity function),即输入恒等于输出,它可以用 I
来表示.
λ表达式是函数式编程的数学基础,也是函数式编程的思想核心。函数式编程所具有的的特点也来源自λ表达式。
2.特点
函数式编程作为一种编程范式,自然会有自己独特之处。本节将列举这些特点,并举例说明与我们平常coding时的不同之处。
2.1 一等公民的函数
当我们说函数是“一等公民”的时候,我们实际上说的是它们和其他对象都一样…
所以就是普通公民。函数真没什么特殊的,你可以像对待任何其他数据类型一样对待它们——把它们存在数组里,当作参数传递,赋值给变量…等等。
函数对应了λ表达式中的抽象,在λ表达式的定义中,抽象与变量平起平坐,并无差异。
虽说在JavaScript语言中,这是基本概念,但在实际应用中仍有如下不合理的代码组织存在:
// 太傻了
const getServerStuff = callback => ajaxCall(json => callback(json));
// 这才像样(其效果跟上面是等价的)
const getServerStuff = ajaxCall;
下面是这样做的原因:
-
这样做除了徒增代码量,提高维护和检索代码的成本外,没有任何用处。
-
如果一个函数被不必要地包裹起来了,而且发生了改动,那么包裹它的那个函数也要做相应的变更。
2.2 纯函数
纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
即:相同输入得到相同输出
举个例子:
// 不纯的
var minimum = 21;
var checkAge = function(age) {
return age >= minimum;
};
// 纯的
var checkAge = function(age) {
var minimum = 21;
return age >= minimum;
};
副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互。
副作用可能包含,但不限于:
- 更改文件系统
- 往数据库插入记录
- 发送一个 http 请求
- 可变数据
- 打印/log
- 获取用户输入
- DOM 查询
- 访问系统状态
- …
这个列表还可以继续写下去。概括来讲,只要是跟函数外部环境发生的交互就都是副作用——这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。
纯函数的优势:
- 可缓存性(Cacheable)
纯函数总能够根据输入来做缓存,实现缓存的一种典型方式是 memoize 技术:
var squareNumber = memoize(function(x){
return x*x; });
squareNumber(4);
//=> 16</