Erlang 20

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,例如listsets

属于最常用的内建函数来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。