所有函数式语言都具有一个重要的特性:把自定义函数作为参数传递给另一个函数。这个函数参数会被绑定到一个变量上,在函数内部可以像使用其他变量一样使用这个变量。如果一个函数的参数是以这种方式传过来的其他函数,则称之为高阶函数(higher-order function)
高阶函数是一种强有力的抽象手段,也是Erlang提供的众多出色工具中需要熟练掌握的一个
函数可以携带并且可以作为参数传递给高阶函数的思想起源于数学,主要来自Lambda演算
本质上,纯Lambda演算中的所有东西都是函数,就连数字、操作符和列表也都是函数。因为所有东西都被表示成函数,所以函数必须能接收其他函数作为参数,还必须能用函数来操作函数
1 | -module(hhfuns). |
1 | Eshell |
Module:Function/Arity
告诉VM去使用这个指定的函数,并把这个函数绑定到一个变量上
1 | increment([]) -> []; |
1 | Eshell |
- 上面2个函数做的事情大致相同:循环遍历列表,在每个元素上应用一个函数(
+
或-
),然后再调用自身
1 | map(_, []) -> []; |
1 | Eshell |
我们将流程中相同的部分抽取出来放在一个单独的函数
map/2
中,这个函数接收另外一个函数作为参数map/2
是一个更聪明的抽象,每当我们想把一个函数应用于列表中的每个元素上时,只需以这个函数为参数调用map/2
即可匿名函数(anonymous functions)又称funs,是在行间定义的一种特殊函数,无需给其取名,从而解决了函数作为参数的那些麻烦问题
正常函数能做的事情,匿名函数基本上也都可以完成,除了不能递归调用自己。语法如下:
1 | fun (Args1) -> |
- 函数式编程可以对非常低层次的代码进行抽象。因此可以完全忽略像循环这种基本概念,从而聚焦在做什么上,而不是怎么做
1 | Eshell |
- 可以把函数的作用域想象成存放所有变量及这些变量对应值的地方。例如
base(A) -> B = A+1
,A
和B
都是base/1
函数作用域的一部分,这意味着在base/1
中的任何地方都可以引用A
和B
,任何地方也包括匿名函数
1 | base(A) -> |
这个例子中,
A
和B
仍然在base/1
的作用域范围之内,所以函数F
可以访问到它们。这是因为F
继承了base/1
的作用域不管匿名函数在哪里,这个被继承的作用域会一直跟随着它,即使把这个匿名函数传递给另外的一个函数
如果函数具有多个参数,但是其中有一个参数一直保持不变,那么此时就很适合使用匿名函数来携带状态
1 | Eshell |
闭包指的是可以让函数引用到它所携带的某些环境(作用域中的值部分)。换句话说,当匿名函数、作用域的概念以及可以携带变量的能力结合在一起时,闭包就出现了
Erlang标准库已经提供了许多基于列表的抽象 参见文档
1 | lists:map/2, lists:filter/2, lists:foldl/3, lists:foldr/3 |