文章目录

Elixir 模式匹配:= 运算符与 case

发布于 2026-04-02 20:49:23 · 浏览 7 次 · 评论 0 条

Elixir 模式匹配:= 运算符与 case

Elixir 的核心特性之一是模式匹配,它贯穿于变量赋值、函数定义、控制流等几乎所有语言结构中。理解 = 运算符和 case 表达式的模式匹配机制,是写出地道 Elixir 代码的关键。


理解 = 不是赋值,而是匹配

在 Elixir 中,=模式匹配运算符,不是传统意义上的“赋值”。它的行为是:尝试让左侧的模式与右侧的值匹配成功;如果匹配失败,则抛出 MatchError

  1. 执行 x = 1
    此时,Elixir 发现左侧 x 是一个未绑定的变量(即“占位符”),于是将 1 绑定给 x。结果:x 的值为 1

  2. 执行 1 = x
    左侧是字面量 1,右侧是已绑定变量 x(值为 1)。Elixir 检查 1 == x 是否成立。成立,匹配成功,无错误。

  3. 执行 2 = x
    左侧是 2,右侧 x 仍为 12 == 1 不成立,抛出 MatchError

这种机制允许你用同一个符号完成“赋值”和“断言”两种操作,具体行为取决于左侧是否包含未绑定变量。


在复杂数据结构中使用模式匹配

模式匹配在处理元组、列表、映射等结构时尤为强大。

元组匹配

point = {1, 2, 3}
{x, y, z} = point

执行上述代码后,x 绑定为 1y2z3。如果左侧元组元素数量或结构不匹配右侧,会报错:

{a, b} = point  # 错误!左侧2个元素,右侧3个

列表匹配

列表可通过 [head | tail] 语法拆分:

list = [1, 2, 3]
[first | rest] = list

执行后,first1rest[2, 3]。你也可以匹配固定长度的列表:

[a, b, c] = list  # a=1, b=2, c=3

但若列表长度不符,匹配失败:

[a, b] = list  # MatchError

映射匹配

映射(Map)匹配只需左侧包含右侧的部分键即可成功:

person = %{name: "Alice", age: 30, city: "Paris"}
%{name: n, age: a} = person

执行后,n"Alice"a30。注意:左侧不需要包含所有键。但如果左侧指定的键在右侧不存在,则失败:

%{name: n, salary: s} = person  # MatchError,因为 person 没有 :salary 键

使用 case 进行条件分支匹配

当需要根据值的不同结构执行不同逻辑时,case 表达式是最清晰的选择。它对表达式的结果依次尝试与每个子句的模式匹配,一旦成功就执行对应代码块。

基本用法

result = case {1, 2, 3} do
  {a, b} -> "Two elements"
  {a, b, c} -> "Three elements: #{a}, #{b}, #{c}"
  _ -> "Other"
end

执行后,result"Three elements: 1, 2, 3"case 从上到下检查模式,第一个匹配成功的子句被执行,其余被忽略。

匹配并附加守卫条件

可在模式后添加 when 守卫,进一步限制匹配条件:

value = 5
case value do
  x when x > 10 -> "Greater than 10"
  x when x > 0 -> "Positive but <= 10"
  _ -> "Zero or negative"
end

执行后,返回 "Positive but <= 10"。守卫中的表达式必须是可快速求值的布尔表达式,不能包含函数调用(除少数允许的如 is_integer/1 等)。

处理可能失败的操作

case 常用于处理返回 {:ok, result}{:error, reason} 的函数:

file_result = File.read("example.txt")
case file_result do
  {:ok, content} -> "File content: #{content}"
  {:error, reason} -> "Failed to read file: #{reason}"
end

执行该代码会根据文件是否存在返回相应消息。这是 Elixir 中处理错误的标准方式,避免了异常流程控制。


避免常见陷阱

  1. 不要混淆 = 和 ==
    = 用于模式匹配,== 用于相等比较。例如:

    {1, 2} = {1, 2}  # 成功(结构相同)
    {1, 2} == {1, 2} # 返回 true(值相等)
  2. 变量在模式中总是绑定,不会比较已有值
    case 或函数参数中,左侧出现的变量名会被视为新绑定,即使同名变量已在外部存在:

    x = 1
    case 2 do
      x -> "This will match, and x inside is 2, not 1!"
    end

    如果你想比较而非绑定,需使用 ^ 引用外部变量:

    x = 1
    case 2 do
      ^x -> "Won't match"
      _ -> "Matches because 2 != 1"
    end
  3. 映射匹配不要求完全一致,但列表和元组要求
    %{a: 1} 能匹配 %{a: 1, b: 2},但 [1] 不能匹配 [1, 2]


实战:解析 API 响应

假设你从 API 收到如下响应:

response = %{
  status: "success",
  data: %{
    user: %{id: 123, name: "Bob"},
    timestamp: 1717020000
  }
}

你想安全提取用户 ID 和名称:

case response do
  %{status: "success", data: %{user: %{id: id, name: name}}} ->
    "User #{name} (ID: #{id}) fetched successfully."
  %{status: "error", message: msg} ->
    "Error: #{msg}"
  _ ->
    "Unexpected response format"
end

执行case 表达式,会精确匹配嵌套结构,并绑定所需字段。任何结构偏差都会进入 _ 默认分支,避免程序崩溃。


总结关键规则

场景 行为
左侧含未绑定变量 变量被绑定为右侧对应部分的值
左侧为字面量或已绑定变量 与右侧对应部分进行相等性检查
case 中的变量 默认视为新绑定(使用 ^ 引用外部变量)
映射模式 只需包含目标映射的子集即可匹配
列表/元组模式 必须与目标结构完全一致(长度、嵌套)

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文