Erlang 从零 · 第 13 课(展开目录)
01 · 先打开 erl02 · 先认常见 term03 · 分清两种文字04 · 变量只接一次05 · 拆开一列,再筛一次06 · 把文字安全变成整数07 · 用 case 选一条路08 · 让函数继续走09 · 把代码放进模块10 · 条件必须说真话11 · 一层一层取数据12 · 文字先说清编码13 · 让函数子句排好队14 · 带着累加器往前走15 · 先看值,再选路16 · 给模块立规矩17 · 让 rebar3 管项目18 · 让结果有固定形状19 · 把磁盘当成会失败的邻居20 · 把函数交给 list21 · record 留在院里22 · 先写模块之间的约定23 · 把 term 形状写出来24 · 小测试与整段路25 · 把应用磨成命令26 · 先把小项目圈住27 · 把真实文件洗干净28 · 公开入口越少越稳29 · 把约定留在源码旁30 · 打包,也留下线索31 · 按最初任务交卷
ERLANG · 基础 · LESSON 1335 分钟

让函数子句排好队

在模块中用模式和 guard 写多个命名函数子句。

01 · 先看完整例子

保存为 fare.erl

运行 c(fare). 后依次调用三种输入。

Erlang
-module(fare).
-export([fee/1]).

%% 具体的儿童规则放在前面
fee(Age) when is_integer(Age), Age >= 0, Age < 12 ->
  {ok, 0};
fee(Age) when is_integer(Age), Age >= 0 ->
  {ok, 20};
fee(_) ->
  {error, invalid_age}.
先对照结果
  • fare:fee(9). 得到 {ok,0}。
  • fare:fee(18). 得到 {ok,20}。
  • fare:fee(<<"nine">>). 得到 {error,invalid_age}。
02 · 回头拆代码

从第一行往下读

  1. 三个子句都属于 fee/1,所以前两条以分号结束。
  2. guard 先确认整数,再检查年龄范围。
  3. 最后的下划线接住所有未匹配输入。
03 · 认清新符号

符号不是暗号

fee(Age) when ...

定义带 guard 的函数子句。

;

同一函数还有下一条子句。

.

当前函数的全部子句结束。

04 · 代码里的概念

把名字和意思对上

01

函数子句

同一个 name/arity 的不同入口,由模式与 guard 区分。

02

guard

when 后可快速检查类型与范围的一组安全表达式。

05 · 再说清楚

这些写法为什么有用

Erlang 命名函数放在模块里。同名、同 arity 的子句必须连在一起,用分号隔开。

运行时从上往下找第一条模式和 guard 都通过的子句。

最具体的规则在前,兜底规则在后。最后一个子句用句点结束。

06 · 自己改一次

合上答案,动手

写出 weather:label/1 的晴天与兜底子句。

练习起点
label(sunny) ->
  ____;
label(_) ->
  ____.

目标结果: 晴天返回 <<"sunny">>,其他输入返回 <<"unknown">>。

卡住了,再看提示

两个空都填写 binary。

运行过以后,再看一种答案
一种答案
%% 具体 atom 放在兜底前
label(sunny) ->
  <<"sunny">>;
label(_) ->
  <<"unknown">>.
想一想:同一个函数的两条子句中间为什么不能用句点?

句点表示函数定义已经结束;下一条同名同 arity 定义会被当作重复函数而编译失败。

带走

记住三句

  1. 1

    函数由 name 与 arity 共同识别。

  2. 2

    同一函数子句必须相邻。

  3. 3

    分号续子句,句点收函数。

本课结束代码也跑过一次,就把这一课收好。