Elixir模式匹配Skill elixir-pattern-matching

Elixir模式匹配技能用于在Elixir编程中实现优雅的声明式代码控制流,包括函数子句、case语句、with语句和解构数据结构。它帮助编写可读、可维护的代码,特别适用于后端开发和函数式编程场景。关键词:Elixir, 模式匹配, 函数式编程, 后端开发, 控制流, 数据解构, 错误处理, 代码优化

后端开发 0 次安装 2 次浏览 更新于 3/25/2026

名称: elixir-pattern-matching 用户可调用: false 描述: 当需要Elixir模式匹配时使用,包括函数子句、case语句、with语句和解构。用于优雅的控制流。 允许工具:

  • Bash
  • Read

Elixir 模式匹配

掌握Elixir中的模式匹配,编写优雅、声明式的代码。 此技能涵盖函数模式、case语句、守卫和解构各种数据结构。

基本模式匹配

# 简单赋值就是模式匹配
x = 1
1 = x  # 这行得通,因为x匹配1

# 使用元组的模式匹配
{:ok, value} = {:ok, "success"}
value  # => "success"

# 如果模式不匹配会引发MatchError
# {:error, _} = {:ok, "success"}  # MatchError

# 使用pin操作符来使用现有值
x = 1
^x = 1  # 有效
# ^x = 2  # MatchError

# 用下划线忽略值
{:ok, _} = {:ok, "any value"}
{_, _, third} = {1, 2, 3}
third  # => 3

函数模式匹配

defmodule Calculator do
  def add(a, b), do: a + b

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

  def describe_tuple({:ok, value}) do
    "Success: #{value}"
  end

  def describe_tuple({:error, reason}) do
    "Error: #{reason}"
  end

  def describe_tuple(_) do
    "Unknown tuple format"
  end
end

# 使用
Calculator.factorial(5)  # => 120
Calculator.describe_tuple({:ok, "done"})  # => "Success: done"

守卫在模式匹配中

defmodule NumberChecker do
  def check(x) when is_integer(x) and x > 0 do
    "Positive integer"
  end

  def check(x) when is_integer(x) and x < 0 do
    "Negative integer"
  end

  def check(0), do: "Zero"

  def check(x) when is_float(x), do: "Float"

  def check(_), do: "Not a number"
end

defmodule Validator do
  def valid_email?(email) when is_binary(email) do
    String.contains?(email, "@")
  end

  def valid_email?(_), do: false

  def in_range?(num, min, max)
      when is_number(num) and num >= min and num <= max do
    true
  end

  def in_range?(_, _, _), do: false
end

Case 语句

defmodule ResponseHandler do
  def handle(response) do
    case response do
      {:ok, data} ->
        {:success, data}

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

      {:error, :timeout} ->
        {:failure, "Request timed out"}

      {:error, reason} ->
        {:failure, "Error: #{inspect(reason)}"}

      _ ->
        {:failure, "Unknown response"}
    end
  end

  def parse_number(str) do
    case Integer.parse(str) do
      {num, ""} -> {:ok, num}
      {num, _remainder} -> {:ok, num}
      :error -> {:error, "Not a valid number"}
    end
  end
end

With 语句用于管道模式匹配

defmodule UserService do
  def create_user(params) do
    with {:ok, email} <- validate_email(params["email"]),
         {:ok, password} <- validate_password(params["password"]),
         {:ok, user} <- insert_user(email, password),
         {:ok, _} <- send_welcome_email(user) do
      {:ok, user}
    else
      {:error, reason} -> {:error, reason}
      _ -> {:error, "Unknown error"}
    end
  end

  defp validate_email(email) when is_binary(email) do
    if String.contains?(email, "@") do
      {:ok, email}
    else
      {:error, "Invalid email"}
    end
  end

  defp validate_email(_), do: {:error, "Email required"}

  defp validate_password(pass) when is_binary(pass) do
    if String.length(pass) >= 8 do
      {:ok, pass}
    else
      {:error, "Password too short"}
    end
  end

  defp validate_password(_), do: {:error, "Password required"}

  defp insert_user(email, password) do
    {:ok, %{id: 1, email: email}}
  end

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

列表模式匹配

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

  def first([head | _tail]), do: head
  def first([]), do: nil

  def second([_, second | _]), do: second
  def second(_), do: nil

  def take_first_three([a, b, c | _rest]) do
    [a, b, c]
  end

  def take_first_three(list), do: list

  def split_at_middle(list) do
    middle = div(length(list), 2)
    {Enum.take(list, middle), Enum.drop(list, middle)}
  end
end

映射模式匹配

defmodule UserHandler do
  def greet(%{name: name, age: age}) do
    "Hello #{name}, you are #{age} years old"
  end

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

  def admin?(%{role: "admin"}), do: true
  def admin?(_), do: false

  def process_user(%{id: id, name: name} = user) do
    # 可以同时使用整个用户和解构的部分
    IO.puts("Processing user #{id}: #{name}")
    user
  end

  def update_status(%{status: old_status} = user, new_status) do
    %{user | status: new_status}
  end
end

defmodule ConfigParser do
  def get_database_url(config) do
    case config do
      %{database: %{host: host, port: port, name: db}} ->
        "postgresql://#{host}:#{port}/#{db}"

      %{database: %{url: url}} ->
        url

      _ ->
        "postgresql://localhost:5432/default"
    end
  end
end

结构体模式匹配

defmodule User do
  defstruct [:id, :name, :email, role: "user"]
end

defmodule StructMatcher do
  def display_user(%User{name: name, email: email}) do
    "#{name} <#{email}>"
  end

  def is_admin?(%User{role: "admin"}), do: true
  def is_admin?(%User{}), do: false

  def update_email(%User{} = user, new_email) do
    %User{user | email: new_email}
  end
end

# 使用
user = %User{id: 1, name: "Alice", email: "alice@example.com"}
StructMatcher.display_user(user)

二进制模式匹配

defmodule BinaryParser do
  def parse_header(<<
        magic::binary-size(4),
        version::16,
        flags::8,
        rest::binary
      >>) do
    %{
      magic: magic,
      version: version,
      flags: flags,
      payload: rest
    }
  end

  def parse_ipv4(<<a, b, c, d>>) do
    "#{a}.#{b}.#{c}.#{d}"
  end

  def parse_utf8(<<codepoint::utf8, rest::binary>>) do
    {codepoint, rest}
  end

  def extract_first_byte(<<first::8, _::binary>>) do
    first
  end
end

Cond 用于多个条件

defmodule GradeCalculator do
  def letter_grade(score) do
    cond do
      score >= 90 -> "A"
      score >= 80 -> "B"
      score >= 70 -> "C"
      score >= 60 -> "D"
      true -> "F"
    end
  end

  def describe_number(n) do
    cond do
      n < 0 -> "negative"
      n == 0 -> "zero"
      n > 0 and n < 10 -> "small positive"
      n >= 10 and n < 100 -> "medium positive"
      true -> "large positive"
    end
  end
end

高级模式匹配

defmodule AdvancedMatcher do
  # 函数参数中的模式匹配与多个子句
  def process([]), do: :empty
  def process([_]), do: :single
  def process([_, _]), do: :pair
  def process([h | t]) when length(t) > 1, do: :multiple

  # 带有映射和守卫的模式匹配
  def format_response(%{status: status, body: body})
      when status >= 200 and status < 300 do
    {:ok, body}
  end

  def format_response(%{status: status, body: body})
      when status >= 400 do
    {:error, body}
  end

  # 嵌套模式匹配
  def extract_user_city(%{
        user: %{address: %{city: city}}
      }) do
    {:ok, city}
  end

  def extract_user_city(_), do: {:error, :no_city}

  # 在for推导式中的模式匹配
  def extract_ok_values(results) do
    for {:ok, value} <- results, do: value
  end
end

何时使用此技能

使用elixir-pattern-matching当您需要:

  • 编写表达力强、声明式的控制流
  • 使用函数子句处理不同数据形状
  • 从复杂数据结构中提取值
  • 在函数边界验证数据格式
  • 实现带有标记元组的干净错误处理
  • 解析二进制数据或协议
  • 构建健壮、可维护的Elixir应用程序
  • 利用Elixir的函数式编程优势
  • 创建清晰、自文档化的代码

最佳实践

  • 尽可能使用模式匹配而不是if/else
  • 从最具体到最一般排序函数子句
  • 使用守卫为模式添加约束
  • 当需要现有值时利用pin操作符
  • 使用下划线表示您不关心的值
  • 优先使用模式匹配而非访问函数
  • 对复杂验证管道使用with语句
  • 保持模式可读且不过于复杂
  • 文档化复杂模式匹配逻辑
  • 一致使用标记元组 {:ok, val} 和 {:error, reason}

常见陷阱

  • 忘记 = 是模式匹配,不是赋值
  • 函数子句排序不正确(具体到一般)
  • 当简单模式可行时过度使用守卫
  • 未处理所有可能的模式情况
  • 因未处理边缘情况而导致MatchError
  • 需要时忘记使用pin操作符
  • 使模式过于复杂和难以阅读
  • 未使用with语句进行多步验证
  • 忽略编译器关于未使用变量的警告
  • 未利用模式匹配编写更简洁的代码

资源