Elixir 从零 · 第 13 课(展开目录)
01 · 先让代码说话02 · 先认六种值03 · 把值装起来04 · 从旧 list 生成新 list05 · 让形状对上06 · 用 case 做选择07 · 把文字安全变成整数08 · 拆开 &1 和管道09 · 把代码放进项目10 · 真假不只靠 true11 · 走进嵌套数据12 · 一字不一定一字节13 · 让函数按形状接活14 · 把一列数收成一个值15 · 给不同问题选不同路16 · 给 map 一张名片17 · 让项目自己验答案18 · 让成功和失败长得一样19 · 读写文件先管好路20 · 只算眼前需要的21 · 同一句话,不同做法22 · 先约好模块会做什么23 · 把数据形状写在门口24 · 测试不只看一条好路25 · 把项目磨成一个命令26 · 定下小项目边界27 · 把真实文字洗干净28 · 只开三扇门29 · 把约定钉在代码旁30 · 打包,也留下脚印31 · 照任务书交卷
ELIXIR · 基础 · LESSON 1330 分钟

让函数按形状接活

用命名函数、多个子句和 guard 把分支写在函数门口。

01 · 先看完整例子

把规则写进函数入口

依次给函数三种输入,观察命中的子句。

Elixir
defmodule Fare do
  # 小于 12 岁走这一条
  def fee(age) when is_integer(age) and age < 12, do: {:ok, 0}

  # 其余非负整数走成人票价
  def fee(age) when is_integer(age) and age >= 0, do: {:ok, 20}

  def fee(_), do: {:error, :invalid_age}
end

Fare.fee(9)
Fare.fee(18)
Fare.fee("九")
先对照结果
  • 三个结果依次是 {:ok, 0}、{:ok, 20}、{:error, :invalid_age}。
02 · 回头拆代码

从第一行往下读

  1. 两个整数子句都先用 is_integer/1 守住类型。
  2. age < 12 比 age >= 0 更具体,所以放在前面。
  3. 最后的 _ 接住没有通过前面规则的输入。
03 · 认清新符号

符号不是暗号

def fee(age) when ...

定义带 guard 的命名函数子句。

_

匹配任何值,但不使用它。

fee/1

函数名是 fee,参数个数是 1。

04 · 代码里的概念

把名字和意思对上

01

函数子句

同一个函数的不同入口。每个入口用模式或 guard 说明自己接什么输入。

02

guard

when 后面的额外条件,只允许使用一组安全表达式。

05 · 再说清楚

这些写法为什么有用

同名、同参数个数的函数可以写多个子句。Elixir 从上往下寻找第一个匹配的子句。

模式先看输入形状,guard 再检查范围。两者放在函数入口,正文就能更短。

最具体的子句放前面,兜底子句放最后。顺序写反,前面的宽规则会挡住后面。

06 · 自己改一次

合上答案,动手

为 Weather.label/1 写出晴天和兜底两个子句。

练习起点
defmodule Weather do
  def label(:sunny), do: ____
  def label(_), do: ____
end

目标结果: Weather.label(:sunny) 得到 "晴",其他输入得到 "未知"。

卡住了,再看提示

右边直接写两个字符串。

运行过以后,再看一种答案
一种答案
defmodule Weather do
  # 具体 atom 放在兜底规则前
  def label(:sunny), do: "晴"
  def label(_), do: "未知"
end
想一想:为什么兜底子句通常放在最后?

它能匹配任何输入。若放在前面,后面的具体子句永远没有机会运行。

带走

记住三句

  1. 1

    函数子句按从上到下的顺序匹配。

  2. 2

    模式看形状,guard 查条件。

  3. 3

    具体规则在前,兜底规则在后。

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