Elixir模式匹配进阶 #

一、case深入 #

1.1 基本模式 #

elixir
case value do
  pattern1 -> result1
  pattern2 -> result2
  _ -> default_result
end

1.2 元组匹配 #

elixir
def handle_result(result) do
  case result do
    {:ok, data} ->
      {:success, process(data)}

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

    {:error, :unauthorized} ->
      {:error, "Unauthorized access"}

    {:error, reason} ->
      {:error, "Failed: #{reason}"}
  end
end

1.3 列表匹配 #

elixir
def process_list(list) do
  case list do
    [] -> "Empty list"
    [single] -> "Single element: #{single}"
    [first, second] -> "Two elements: #{first}, #{second}"
    [head | tail] -> "Head: #{head}, Tail length: #{length(tail)}"
  end
end

1.4 映射匹配 #

elixir
def handle_user(user) do
  case user do
    %{role: :admin, name: name} ->
      "Admin: #{name}"

    %{role: :user, active: true, name: name} ->
      "Active user: #{name}"

    %{role: :user, active: false} ->
      "Inactive user"

    %{name: name} ->
      "Unknown role: #{name}"
  end
end

1.5 嵌套匹配 #

elixir
def handle_config(config) do
  case config do
    %{
      database: %{host: host, port: port},
      cache: %{enabled: true, ttl: ttl}
    } ->
      "DB: #{host}:#{port}, Cache TTL: #{ttl}"

    %{
      database: %{host: host, port: port},
      cache: %{enabled: false}
    } ->
      "DB: #{host}:#{port}, No cache"

    _ ->
      "Invalid config"
  end
end

1.6 二进制匹配 #

elixir
def parse_packet(data) do
  case data do
    <<1::size(8), length::size(16), payload::binary-size(length), rest::binary>> ->
      {:ok, %{type: 1, payload: payload}, rest}

    <<2::size(8), id::size(32), message::binary>> ->
      {:ok, %{type: 2, id: id, message: message}, ""}

    <<type::size(8), _rest::binary>> ->
      {:error, "Unknown packet type: #{type}"}
  end
end

二、守卫子句深入 #

2.1 基本语法 #

elixir
case value do
  pattern when guard -> result
end

2.2 类型守卫 #

elixir
def process(value) do
  case value do
    x when is_integer(x) -> "Integer: #{x}"
    x when is_float(x) -> "Float: #{x}"
    x when is_binary(x) -> "String: #{x}"
    x when is_list(x) -> "List with #{length(x)} elements"
    x when is_map(x) -> "Map with #{map_size(x)} keys"
    x when is_atom(x) -> "Atom: #{x}"
    _ -> "Unknown type"
  end
end

2.3 比较守卫 #

elixir
def categorize_number(n) do
  case n do
    x when x < 0 -> "Negative"
    0 -> "Zero"
    x when x > 0 and x < 10 -> "Small positive"
    x when x >= 10 and x < 100 -> "Medium positive"
    x when x >= 100 -> "Large positive"
  end
end

2.4 列表守卫 #

elixir
def process_list(list) do
  case list do
    [] -> "Empty"
    [_] -> "Single element"
    [_, _] -> "Two elements"
    list when length(list) > 10 -> "Long list"
    _ -> "Normal list"
  end
end

2.5 多条件守卫 #

elixir
def validate_user(user) do
  case user do
    %{age: age} when age >= 18 and age <= 65 ->
      {:ok, "Working age"}

    %{age: age} when age < 18 ->
      {:error, "Too young"}

    %{age: age} when age > 65 ->
      {:ok, "Retirement age"}

    _ ->
      {:error, "Invalid user"}
  end
end

2.6 成员检查守卫 #

elixir
def handle_role(role) do
  case role do
    r when r in [:admin, :super_admin] -> :full_access
    r when r in [:moderator, :editor] -> :limited_access
    :user -> :basic_access
    _ -> :no_access
  end
end

三、with表达式 #

3.1 基本语法 #

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

elixir
with pattern1 <- expr1,
     pattern2 <- expr2,
     pattern3 <- expr3 do
  success_block
else
  pattern4 -> error_block
end

3.2 基本示例 #

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

3.3 提前退出 #

elixir
iex(1)> with {:ok, a} <- {:ok, 1},
...>      {:error, reason} <- {:error, "failed"},
...>      {:ok, c} <- {:ok, 3} do
...>   a + c
...> else
...>   {:error, reason} -> {:error, reason}
...> end
{:error, "failed"}

3.4 实际应用 #

elixir
defmodule UserService do
  def register_user(params) do
    with {:ok, validated} <- validate_params(params),
         {:ok, user} <- create_user(validated),
         {:ok, token} <- generate_token(user),
         {:ok, _} <- send_welcome_email(user) do
      {:ok, %{user: user, token: token}}
    end
  end

  defp validate_params(params) do
    if params[:email] && params[:password] do
      {:ok, params}
    else
      {:error, :invalid_params}
    end
  end

  defp create_user(params) do
    {:ok, %{id: 1, email: params[:email]}}
  end

  defp generate_token(user) do
    {:ok, "token-#{user.id}"}
  end

  defp send_welcome_email(user) do
    {:ok, :sent}
  end
end

3.5 else子句 #

elixir
def fetch_and_process(user_id) do
  with {:ok, user} <- fetch_user(user_id),
       {:ok, posts} <- fetch_posts(user),
       {:ok, comments} <- fetch_comments(posts) do
    {:ok, %{user: user, posts: posts, comments: comments}}
  else
    {:error, :not_found} ->
      {:error, "User not found"}

    {:error, :no_posts} ->
      {:ok, %{user: user, posts: [], comments: []}}

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

3.6 无else子句 #

如果没有else子句,失败的值会直接返回:

elixir
iex(1)> with {:ok, x} <- {:error, :not_found} do
...>   x * 2
...> end
{:error, :not_found}

3.7 with与模式匹配 #

elixir
def process_config(config) do
  with {:ok, host} <- Map.fetch(config, :host),
       {:ok, port} <- Map.fetch(config, :port),
       {:ok, db} <- Map.fetch(config, :database) do
    {:ok, %{host: host, port: port, database: db}}
  else
    :error -> {:error, "Missing configuration"}
  end
end

四、函数头守卫 #

4.1 多子句函数 #

elixir
defmodule Math do
  def abs(x) when x < 0, do: -x
  def abs(x), do: x

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

  def factorial(0), do: 1
  def factorial(n) when n > 0, do: n * factorial(n - 1)
end

4.2 参数模式匹配 #

elixir
defmodule ListUtils do
  def first([head | _]), do: head
  def first([]), do: nil

  def last([x]), do: x
  def last([_ | tail]), do: last(tail)
  def last([]), do: nil

  def nth([head | _], 0), do: head
  def nth([_ | tail], n) when n > 0, do: nth(tail, n - 1)
  def nth([], _), do: nil
end

4.3 复杂守卫 #

elixir
defmodule Validator do
  def validate(%{email: email, password: password})
      when is_binary(email) and is_binary(password) and byte_size(password) >= 8 do
    {:ok, %{email: email, password: password}}
  end

  def validate(%{email: _, password: password}) when byte_size(password) < 8 do
    {:error, "Password too short"}
  end

  def validate(_) do
    {:error, "Invalid input"}
  end
end

五、模式匹配最佳实践 #

5.1 优先使用模式匹配 #

elixir
def bad_example(user) do
  if user[:role] == :admin do
    if user[:active] do
      "Active admin"
    else
      "Inactive admin"
    end
  else
    "Not admin"
  end
end

def good_example(user) do
  case user do
    %{role: :admin, active: true} -> "Active admin"
    %{role: :admin} -> "Inactive admin"
    _ -> "Not admin"
  end
end

5.2 使用with处理链式操作 #

elixir
def bad_example(params) do
  case validate(params) do
    {:ok, validated} ->
      case create(validated) do
        {:ok, created} ->
          case notify(created) do
            {:ok, _} -> {:ok, created}
            error -> error
          end
        error -> error
      end
    error -> error
  end
end

def good_example(params) do
  with {:ok, validated} <- validate(params),
       {:ok, created} <- create(validated),
       {:ok, _} <- notify(created) do
    {:ok, created}
  end
end

5.3 使用守卫简化逻辑 #

elixir
def bad_example(list) do
  if is_list(list) and length(list) > 0 do
    hd(list)
  else
    nil
  end
end

def good_example([head | _]), do: head
def good_example([]), do: nil

六、总结 #

本章学习了:

特性 用途
case 多分支模式匹配
守卫子句 模式匹配中的条件约束
with 链式模式匹配
函数头守卫 函数参数验证

准备好学习函数了吗?让我们进入下一章。

最后更新:2026-03-27