Elixir 从零 · 第 05 课(展开目录)
让形状对上
把 `=` 当作匹配,拆开 tuple、list 和 map。
从三种集合里拆值
先从左边读形状,再去右边找对应的值。
# 先把预期标签记下来
expected_tag = :ok
# ^ 使用 expected_tag 的旧值核对标签;下划线忽略第三个值
{^expected_tag, score, _} = {:ok, 92, :midterm}
# 拆出 list 的第一个元素和其余部分
[first | rest] = ["桃", "李", "杏"]
# map 模式只写需要的 key
%{name: name} = %{name: "阿青", age: 12}
{score, first, rest, name}score得到92。first得到"桃",rest得到["李", "杏"]。name得到"阿青"。
从第一行往下读
^expected_tag不会重新绑定名字。它要求 tuple 第一个值等于已有的:ok;第三个值被_忽略。[first | rest]只能匹配非空 list。竖线把第一个值与剩余 list 分开。- map 模式可以只匹配需要的 key;右边多出的
age不妨碍匹配。
符号不是暗号
=匹配左右两边。左边的新名字会绑定到对应值。
_这个位置的值不需要保存。
[head | tail]把非空 list 拆成第一个元素和剩余元素。
^name用 name 已有的值核对这个位置。读作 pin name。
把名字和意思对上
模式
写在左边的形状,用来检查并拆开右边的数据。
解构
一次从集合里取出多个值。
匹配失败
左右形状或固定值对不上时,Elixir 会给出 MatchError。
pin 运算符
^name 在模式中使用名字原来绑定的值,不让这个位置接住一个新值。
这些写法为什么有用
Elixir 的 = 不只是“赋值”。它会检查左右两边能不能按同一种形状对上。
左边像一张空表格。右边的数据填进去。位置、标签或 key 对不上时,匹配就失败。
这种写法常用来拆数据。你只写想取出的部分,剩下的可以交给下划线 _。
模式里的普通名字会接住新值。如果要拿一个已经绑定的名字来核对,就在前面写 pin 符号 ^。^expected_tag 的意思是“这里必须等于旧的 expected_tag”。
为什么这样写
为什么不把 = 只当作赋值?
模式把“检查形状”和“取出数据”合在一次操作里。函数入口、case 和 receive 因此能用同一套读法。
跨一次新的匹配时,两门语言规则不同:Elixir 的名字默认可以重新绑定,要用 ^name 才核对旧值;Erlang 的变量一旦绑定,再出现在模式里就会核对。
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- 局部变量赋值,再用 getter 或 instanceof 检查对象。
- BEAM 在意什么
- 模式直接检查 tuple、list 或 map 的形状并取值。
- 别带错直觉
- 这里不是方法重载,也不会修改右边的数据。
Python
- 熟悉的起点
- 解包和 match/case 已有相近感觉。
- BEAM 在意什么
- 数据不会被原地改写,模式也是函数子句和消息协议的日常工具。
- 别带错直觉
- Elixir 可以重新绑定名字,但不会改掉原值;Erlang 的已绑定变量会用来核对。
JavaScript
- 熟悉的起点
- 数组、对象解构主要负责取值。
- BEAM 在意什么
- BEAM 模式还会核对固定标签;不匹配会走别的子句或失败。
- 别带错直觉
- 对象解构的默认值与 BEAM 模式不是同一套规则。
合上答案,动手
先绑定预期标签,再从 {:weather, "西安", 28} 中核对标签并拆出城市和温度。
# 这次只接受 weather 消息
expected_tag = :weather
# 用 pin 核对已有标签,给后两个位置起名字
{^____, ____, ____} = {:weather, "西安", 28}
# 返回城市和温度
{city, temperature}目标结果: 得到 {"西安", 28}。
卡住了,再看提示
第一个位置写 ^expected_tag,后两个位置写 city 和 temperature。
运行过以后,再看一种答案
# 先保存要核对的标签
expected_tag = :weather
# ^expected_tag 必须匹配,两个新名字接住数据
{^expected_tag, city, temperature} = {:weather, "西安", 28}
# 查看拆出的值
{city, temperature}想一想:`{:ok, value} = {:error, 3}` 会成功吗?
不会。左边要求第一个值是 :ok,右边却是 :error,所以会出现 MatchError。
记住三句
- 1
=会检查形状,不只是把值放进名字。 - 2
固定值负责核对,新名字负责接住数据。
- 3
^name用旧值核对;不需要的值可以用_忽略。