Elixir 从零 · 第 05 课(展开目录)
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 0525 分钟

让形状对上

把 `=` 当作匹配,拆开 tuple、list 和 map。

01 · 先看完整例子

从三种集合里拆值

先从左边读形状,再去右边找对应的值。

Elixir
# 先把预期标签记下来
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 得到 "阿青"。
02 · 回头拆代码

从第一行往下读

  1. ^expected_tag 不会重新绑定名字。它要求 tuple 第一个值等于已有的 :ok;第三个值被 _ 忽略。
  2. [first | rest] 只能匹配非空 list。竖线把第一个值与剩余 list 分开。
  3. map 模式可以只匹配需要的 key;右边多出的 age 不妨碍匹配。
03 · 认清新符号

符号不是暗号

=

匹配左右两边。左边的新名字会绑定到对应值。

_

这个位置的值不需要保存。

[head | tail]

把非空 list 拆成第一个元素和剩余元素。

^name

用 name 已有的值核对这个位置。读作 pin name。

04 · 代码里的概念

把名字和意思对上

01

模式

写在左边的形状,用来检查并拆开右边的数据。

02

解构

一次从集合里取出多个值。

03

匹配失败

左右形状或固定值对不上时,Elixir 会给出 MatchError。

04

pin 运算符

^name 在模式中使用名字原来绑定的值,不让这个位置接住一个新值。

05 · 再说清楚

这些写法为什么有用

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 模式不是同一套规则。
06 · 自己改一次

合上答案,动手

先绑定预期标签,再从 {: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. 1

    = 会检查形状,不只是把值放进名字。

  2. 2

    固定值负责核对,新名字负责接住数据。

  3. 3

    ^name 用旧值核对;不需要的值可以用 _ 忽略。

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