Elixir模式匹配 #

一、匹配操作符 #

1.1 基本概念 #

Elixir使用 = 作为匹配操作符,而非赋值操作符:

elixir
iex(1)> x = 1
1

iex(2)> 1 = x
1

iex(3)> 2 = x
** (MatchError) no match of right hand side value: 1

1.2 匹配规则 #

左边是模式,右边是值,匹配成功则绑定变量:

elixir
iex(1)> {a, b, c} = {1, 2, 3}
{1, 2, 3}

iex(2)> a
1

iex(3)> b
2

iex(4)> c
3

1.3 单向匹配 #

匹配是单向的,右边不能包含未绑定变量:

elixir
iex(1)> {a, b} = {1, 2}
{1, 2}

iex(2)> {a, b} = {1, c}
** (CompileError) undefined variable "c"

二、元组匹配 #

2.1 基本匹配 #

elixir
iex(1)> {a, b, c} = {1, 2, 3}
{1, 2, 3}

iex(2)> {a, b} = {1, 2, 3}
** (MatchError) no match of right hand side value: {1, 2, 3}

2.2 忽略值 #

使用 _ 忽略不需要的值:

elixir
iex(1)> {a, _, c} = {1, 2, 3}
{1, 2, 3}

iex(2)> a
1

iex(3)> c
3

2.3 嵌套匹配 #

elixir
iex(1)> {a, {b, c}} = {1, {2, 3}}
{1, {2, 3}}

iex(2)> a
1

iex(3)> b
2

iex(4)> c
3

2.4 常见模式 #

elixir
iex(1)> {:ok, result} = {:ok, "Success"}
{:ok, "Success"}

iex(2)> result
"Success"

iex(3)> {:error, reason} = {:error, "Not found"}
{:error, "Not found"}

iex(4)> reason
"Not found"

三、列表匹配 #

3.1 头尾匹配 #

elixir
iex(1)> [head | tail] = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

iex(2)> head
1

iex(3)> tail
[2, 3, 4, 5]

3.2 多元素匹配 #

elixir
iex(1)> [a, b | rest] = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

iex(2)> a
1

iex(3)> b
2

iex(4)> rest
[3, 4, 5]

3.3 空列表匹配 #

elixir
iex(1)> [] = []
[]

iex(2)> [head | tail] = []
** (MatchError) no match of right hand side value: []

3.4 递归处理列表 #

elixir
defmodule ListUtils do
  def sum([]), do: 0
  def sum([head | tail]), do: head + sum(tail)

  def length([]), do: 0
  def length([_ | tail]), do: 1 + length(tail)

  def map([], _func), do: []
  def map([head | tail], func), do: [func.(head) | map(tail, func)]
end

四、映射匹配 #

4.1 基本匹配 #

elixir
iex(1)> %{name: name, age: age} = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> name
"Alice"

iex(3)> age
30

4.2 部分匹配 #

映射匹配只需要匹配存在的键:

elixir
iex(1)> %{name: name} = %{name: "Alice", age: 30, city: "NYC"}
%{age: 30, city: "NYC", name: "Alice"}

iex(2)> name
"Alice"

4.3 嵌套匹配 #

elixir
iex(1)> %{address: %{city: city}} = %{name: "Alice", address: %{city: "NYC", zip: "10001"}}
%{address: %{city: "NYC", zip: "10001"}, name: "Alice"}

iex(2)> city
"NYC"

4.4 动态键匹配 #

elixir
iex(1)> %{key => value} = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(2)> key
:age

iex(3)> value
30

4.5 混合匹配 #

elixir
iex(1)> %{name: name, rest} = %{name: "Alice", age: 30, city: "NYC"}
%{age: 30, city: "NYC", name: "Alice"}

iex(2)> name
"Alice"

iex(3)> rest
%{age: 30, city: "NYC"}

五、二进制匹配 #

5.1 基本匹配 #

elixir
iex(1)> <<a, b, c>> = <<1, 2, 3>>
<<1, 2, 3>>

iex(2)> a
1

iex(3)> b
2

iex(4)> c
3

5.2 指定位数 #

elixir
iex(1)> <<a::size(8), b::size(8)>> = <<255, 128>>
<<255, 128>>

iex(2)> a
255

iex(3)> b
128

5.3 字符串头部匹配 #

elixir
iex(1)> <<first::utf8, rest::binary>> = "Hello"
"Hello"

iex(2)> first
72

iex(3)> rest
"ello"

iex(4)> <<first::utf8, _rest::binary>> = "你好"
"你好"

iex(5)> first
20320

5.4 解析二进制数据 #

elixir
defmodule BinaryParser do
  def parse_header(<<
    magic::binary-size(4),
    version::size(32),
    flags::size(32),
    rest::binary
  >>) do
    %{
      magic: magic,
      version: version,
      flags: flags,
      data: rest
    }
  end
end

六、Pin操作符 #

6.1 基本用法 #

使用 ^ 钉住变量,进行值比较而非重新绑定:

elixir
iex(1)> x = 1
1

iex(2)> ^x = 1
1

iex(3)> ^x = 2
** (MatchError) no match of right hand side value: 2

6.2 在模式中使用 #

elixir
iex(1)> expected = 1
1

iex(2)> {^expected, result} = {1, "Success"}
{1, "Success"}

iex(3)> result
"Success"

iex(4)> {^expected, result} = {2, "Success"}
** (MatchError) no match of right hand side value: {2, "Success"}

6.3 在函数参数中使用 #

elixir
defmodule Example do
  def greet(%{name: name, language: "elixir"}) do
    "Welcome to Elixir, #{name}!"
  end

  def greet(%{name: name, language: "erlang"}) do
    "Welcome to Erlang, #{name}!"
  end

  def greet(%{name: name}) do
    "Hello, #{name}!"
  end
end

6.4 在映射键中使用 #

elixir
iex(1)> key = :name
:name

iex(2)> %{^key => value} = %{name: "Alice", age: 30}
%{age: 30, name: "Alice"}

iex(3)> value
"Alice"

七、函数中的模式匹配 #

7.1 多子句函数 #

elixir
defmodule Math do
  def zero?(0), do: true
  def zero?(_), do: false

  def sign(n) when n > 0, do: 1
  def sign(n) when n < 0, do: -1
  def sign(0), do: 0
end

7.2 参数解构 #

elixir
defmodule User do
  def full_name(%{first: first, last: last}) do
    "#{first} #{last}"
  end

  def greet(%{name: name, age: age}) when age >= 18 do
    "Hello, #{name}!"
  end

  def greet(%{name: name}) do
    "Hi, #{name}!"
  end
end

7.3 递归函数 #

elixir
defmodule Recursion do
  def factorial(0), do: 1
  def factorial(n) when n > 0, do: n * factorial(n - 1)

  def fib(0), do: 0
  def fib(1), do: 1
  def fib(n) when n > 1, do: fib(n - 1) + fib(n - 2)

  def reverse([]), do: []
  def reverse([head | tail]), do: reverse(tail) ++ [head]
end

八、case表达式 #

8.1 基本语法 #

elixir
iex(1)> case {1, 2, 3} do
...>   {1, x, 3} -> "Matched with x = #{x}"
...>   {4, 5, 6} -> "Won't match"
...>   _ -> "Match anything"
...> end
"Matched with x = 2"

8.2 守卫子句 #

elixir
iex(1)> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 -> "x is positive"
...>   {1, x, 3} when x < 0 -> "x is negative"
...>   _ -> "No match"
...> end
"x is positive"

8.3 处理结果 #

elixir
def handle_response(response) do
  case response do
    {:ok, data} ->
      process_data(data)

    {:error, :not_found} ->
      {:error, "Resource not found"}

    {:error, reason} ->
      {:error, reason}
  end
end

九、with表达式 #

9.1 基本语法 #

with 用于链式匹配,任一匹配失败则提前退出:

elixir
iex(1)> with {:ok, a} <- {:ok, 1},
...>      {:ok, b} <- {:ok, 2},
...>      {:ok, c} <- {:ok, 3} do
...>   a + b + c
...> end
6

9.2 处理错误 #

elixir
def fetch_user(id) do
  with {:ok, user} <- Repo.get(User, id),
       {:ok, posts} <- Repo.get_posts(user),
       {:ok, comments} <- Repo.get_comments(posts) do
    {:ok, %{user: user, posts: posts, comments: comments}}
  end
end

9.3 else子句 #

elixir
def create_user(params) do
  with {:ok, user} <- validate_params(params),
       {:ok, user} <- insert_user(user),
       {:ok, token} <- generate_token(user) do
    {:ok, %{user: user, token: token}}
  else
    {:error, :invalid_params} ->
      {:error, "Invalid parameters"}

    {:error, :duplicate_email} ->
      {:error, "Email already exists"}

    {:error, reason} ->
      {:error, reason}
  end
end

十、总结 #

本章学习了:

模式 示例 说明
元组匹配 {a, b} = {1, 2} 解构元组
列表匹配 [head | tail] = [1, 2, 3] 头尾分解
映射匹配 %{name: name} = user 提取键值
二进制匹配 <<a, b>> = <<1, 2>> 解析二进制
Pin操作符 ^x = 1 值比较
case表达式 case x do ... end 多分支匹配
with表达式 with {:ok, x} <- f() do ... end 链式匹配

准备好学习集合类型了吗?让我们进入下一章。

最后更新:2026-03-27