6.函数 | 6. Functions
6函数
6.1函数声明语法
函数声明
是由分号分隔,并且通过周期终止功能的条款的序列(.)。
一个函数子句由一个子句头和一个子句体组成,它们之间用->。
子句头
由函数名称,参数列表和以关键字开头
的可选保护序列组成when
:
Name(Pattern11,...,Pattern1N) [when GuardSeq1] ->
Body1;
...;
Name(PatternK1,...,PatternKN) [when GuardSeqK] ->
BodyK.
函数名是一个原子。每个论点都是一个模式。
参数的个数N
是元数
的函数。函数由模块名称,函数名称和元素唯一地定义。也就是说,两个具有相同名称且在相同模块中但功能不同的功能是两种不同的功能。
f
在模块中命名的功能通常用m
符号N
表示m:f/N
。
子句体
由用逗号(,)分隔的一系列表达式组成:
Expr1,
...,
ExprN
在中描述了有效的Erlang表达式和保护序列Expressions
。
例子:
fact(N) when N>0 -> % first clause head
N * fact(N-1 % first clause body
fact(0) -> % second clause head
1. % second clause body
6.2函数评估
当函数m:f/N
被调用时,首先定位函数的代码。如果找不到该函数,undef
则会发生运行时错误。请注意,该函数必须导出为在其定义的模块外部可见。
如果找到该函数,则会顺序扫描函数子句,直到找到满足以下两个条件的子句为止:
- 子句头中的模式可以与给定的参数成功匹配。
- 守卫序列,如果有的话,是正确的。
如果找不到这样的子句,function_clause
则会发生运行时错误。
如果找到这样的条款,则评估相应的条款主体。也就是说,主体中的表达式将按顺序评估,并返回最后一个表达式的值。
考虑函数fact
*
-module(m).
-export([fact/1]).
fact(N) when N>0 ->
N * fact(N-1
fact(0) ->
1.
假设您想要计算1的阶乘:
1> m:fact(1).
评估从第一个条款开始。该模式N与参数1相匹配。匹配成功并且guard(N>0)为真,因此N绑定为1,并计算相应的正文:
N * fact(N-1) => (N is bound to 1)
1 * fact(0)
现在,fact(0)被调用,并且函数子句再次被顺序扫描。首先,模式N匹配为0.匹配成功,但guard(N>0)为false。第二,模式0与0匹配。匹配成功并评估主体:
1 * fact(0) =>
1 * 1 =>
1
评估已成功并m:fact(1)
返回1。
如果m:fact/1
以负数作为参数调用,则不匹配子句头。一function_clause
出现运行错误。
6.3尾递归
如果函数体的最后一个表达式是一个函数调用,则完成一个尾递归
调用。这是为了确保不消耗系统资源,例如调用堆栈。这意味着如果使用尾递归
调用,则可以执行无限循环。
例子:
loop(N) ->
io:format("~w~n", [N]),
loop(N+1).
较早的因子例子可以作为反例。它不是尾递归的,因为乘法是对递归调用的结果完成的fact(N-1)
。
6.4内置函数(BIF)
BIF在运行时系统中以C代码实现。BIF在Erlang中执行难以或不可能实现的事情。大多数BIF都属于该模块,erlang
但也有属于其他几个模块的BIF,例如lists
和ets
。
属于最常用的内建函数来erlang(3)
是自动导入
。它们不需要以模块名称作为前缀。erlang(3)
ERTS 中的模块中指定了哪些自动导入
的BIF。例如,atom_to_list
可以在不指定模块名称的情况下调用标准类型的转换BIF,例如和守卫中允许的BIF。
例子:
1> tuple_size{a,b,c}).
3
2> atom_to_list('Erlang').
"Erlang"
注意,它通常是在讨论'BIF'时引用的一组自动导入的BIF。