平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Erlang中的函数与流程控制介绍”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
一:函数
1:在Erlang中,【名字相同但参数数目不同】的两个函数是完全不同的函数。
2:其他模块内的函数用完全限定名称 被调用:
reverse_sort(L) ->
lists1:reverse(sort(L)).
sort(L) ->
lists:sort(L).
4:每个函数都由一组子句组成。子句间以分号“;”分隔。每个子句都包含一个子句头部、一个可选的保护式和子句主体。子句的头部包含一个函数名和一组以逗号分隔的参数当函数调用发生时,将会按顺序对函数定义中的子句头部依次进行匹配。对保护式求值时所有的断言都将被求值。若所有断言都为真,则保护式成立,否则就失败。保护式中各个断言的求值顺序是不确定的。
结合项目来看,若保护式成立,则会对子句的主体进行求值。如果保护式失败,则尝试下一个候选子句。一旦子句的头部和保护式都匹配成功,系统将指定这条子句同时对其主体求值。子句首部模式与保护式的组合能够唯一确定一个正确的子句。
保护式断言的完整集合如下所示:
| 保护式 | 成立条件 |
|---|---|
| atom(X) | X 是一个原子式 |
| constant(X) | X 不是列表或元组 |
| float(X) | X 是一个浮点数 |
| integer(X) | X 是一个整数 |
| list(X) | X 是一个列表或 [] |
| number | X 是一个整数或浮点数 |
| pid(X) | X 是一个进程标识符 |
| port(X) | X 是一个端口 |
| reference(X) | X 是一个引用 |
| tuple(X) | X 是一个元组 |
| binary(X) | X 是一段二进制数据 |
另外,一些BIF和算术表达式的组合也能够作为保护式。它们是:
| 运算符 | 描述 | 类型 |
|---|---|---|
| X > Y | X 大于Y | coerce |
| X < Y | X 小于Y | coerce |
| X =< Y | X 小于或等于Y | coerce |
| X >= Y | X 大于或等于Y | coerce |
| X == Y | X 等于Y | coerce |
| X /= Y | X 不等于Y | coerce |
| X =:= Y | X 等于Y | exact |
| X =/= Y | X 不等于Y | exact |
落到代码里,比较运算符工作机制如下所示:首先对运算符两边求值(如,在表达式两边存在算术表达式或包含BIF保护式函数时);随后再进行比较。
为便于进行比较,定义如下所示的偏序关系:
元组首先按大小排序,随后再按元素排序。列表的比较顺序是先头部,后尾部。
落到代码里,若比较运算符的两个参数都是数值类型且运算符为coerce型,则如果一个参数是integer另一个是float,那么integer将被转换为float再进行比较。
exact类型的运算符则不做这样的转换。
所以5.0 == 1 + 4为真,而5.0 =:= 4 + 1为假。
保护函数子句示例:
二、流程控制
case语句
case表达式允许在子句主体内部于多个选项中进行选择,语法如下所示:
从实现思路看,至少得有一个模式必须得以匹配——否则就会产生一个运行时错误同时引发第??章中的错误处理机制。
在这个场景下,举个例子,比方说我们我有个函数allocate(Resource)用来分配某种资源Resource。假设这个函数只得到{yes, Address}或no。这样,这个函数便能够放在一个case结构里:
结合项目来看,在Sequence 1 ...里,变量Address已经被绑定在了allocate/1的得到结果上。
结合项目来看,为便于避免匹配错误的发生,我们常常追加一个必会匹配的模式作为case原语的最后一个分支:
IF
if表达式的语法如下所示:
算术表达式
算术表达式由以下运算符构成:
| 运算符 | 描述 | 类型 | 操作数类型 | 优先级 |
|---|---|---|---|---|
| + X | + X | 单目 | 混合 | 1 |
| - X | - X | 单目 | 混合 | 1 |
| X * Y | X * Y | 双目 | 混合 | 2 |
| X / Y | X / Y (浮点除法) | 双目 | 混合 | 2 |
| X div Y | X 整除Y | 双目 | 整数 | 2 |
| X rem Y | X 除以Y 的余数 | 双目 | 整数 | 2 |
| X band Y | X 与Y 的位与 | 双目 | 整数 | 2 |
| X + Y | X + Y | 双目 | 混合 | 3 |
| X - Y | X - Y | 双目 | 混合 | 3 |
| X bor Y | X 与Y 位或 | 双目 | 整数 | 3 |
| X bxor Y | X 与Y 的位算数异或 | 双目 | 整数 | 3 |
| X bsl N | X 算数左移N 位 | 双目 | 整数 | 3 |
| X bsr N | X 右移N 位 | 双目 | 整数 | 3 |
结合项目来看,单目 运算符有一个参数,双目 运算符有两个参数。混合 意味着参数即能够是integer 也能够是float。单目运算符的得到值与其参数类型相同。
在这个场景下,双目混合运算符(即*、-、+)在参数都是integer 时得到类型为integer 的对象,在参数至少包含一个float 时得到一个float。浮点除法运算符/ 总是得到一个float。
结合项目来看,双目整数运算符(即band、div、rem、bor、bxor、bsl、bsr)的参数必须是整数,其得到值也是整数。
结合项目来看,求值顺序取决于运算符的优先级:首先计算第1优先级的运算符,随后是第2优先级,以此类推。括号内的表达式优先求值。
优先级相同的运算符从左到右进行求值。