パターンマッチング完全ガイド
パターンマッチング完全ガイド
Section titled “パターンマッチング完全ガイド”Elixirのパターンマッチング機能を、実務で使える実装例とともに詳しく解説します。
1. パターンマッチングとは
Section titled “1. パターンマッチングとは”パターンマッチングの特徴
Section titled “パターンマッチングの特徴”パターンマッチングは、Elixirの最も重要な機能の一つです。データの構造を分解し、条件分岐を簡潔に記述できます。
パターンマッチングの用途 ├─ データの分解 ├─ 条件分岐 ├─ 関数の多重定義 └─ エラーハンドリングなぜパターンマッチングが必要か
Section titled “なぜパターンマッチングが必要か”問題のある構成(パターンマッチングなし):
# 問題: 複雑な条件分岐def process_result(result) do if is_tuple(result) do if tuple_size(result) == 2 do status = elem(result, 0) data = elem(result, 1) if status == :ok do handle_success(data) else handle_error(data) end end endend解決: パターンマッチングによる簡潔な記述
# 解決: パターンマッチングで簡潔にdef process_result({:ok, data}) do handle_success(data)end
def process_result({:error, reason}) do handle_error(reason)end2. 基本的なパターンマッチング
Section titled “2. 基本的なパターンマッチング”代入とパターンマッチング
Section titled “代入とパターンマッチング”# 基本的な代入x = 1
# タプルのパターンマッチング{a, b} = {1, 2}# a => 1, b => 2
# リストのパターンマッチング[head | tail] = [1, 2, 3, 4]# head => 1, tail => [2, 3, 4]
# マップのパターンマッチング%{name: name, age: age} = %{name: "Alice", age: 30}# name => "Alice", age => 30ピン演算子(^)
Section titled “ピン演算子(^)”# 変数の値を固定(再代入を防ぐ)x = 1^x = 1 # 成功^x = 2 # MatchError
# パターンマッチングでの使用x = 1{^x, y} = {1, 2} # 成功: xは1で固定{^x, y} = {2, 3} # MatchError: xは1なので2とマッチしない3. 関数でのパターンマッチング
Section titled “3. 関数でのパターンマッチング”関数の多重定義
Section titled “関数の多重定義”defmodule Math do # 0の場合 def factorial(0), do: 1
# 正の数の場合 def factorial(n) when n > 0 do n * factorial(n - 1) end
# 負の数の場合 def factorial(_n) do {:error, "Factorial is not defined for negative numbers"} endend
Math.factorial(5) # => 120Math.factorial(0) # => 1Math.factorial(-1) # => {:error, "Factorial is not defined for negative numbers"}ガード節の使用
Section titled “ガード節の使用”defmodule User do def process_user(%{age: age}) when age >= 18 do {:ok, "Adult user"} end
def process_user(%{age: age}) when age < 18 do {:ok, "Minor user"} end
def process_user(_) do {:error, "Invalid user"} endend4. case文でのパターンマッチング
Section titled “4. case文でのパターンマッチング”case文の基本
Section titled “case文の基本”case File.read("config.json") do {:ok, content} -> IO.puts("File read successfully") process_content(content)
{:error, :enoent} -> IO.puts("File not found")
{:error, reason} -> IO.puts("Error: #{inspect(reason)}")endガード節との組み合わせ
Section titled “ガード節との組み合わせ”case {1, 2, 3} do {1, x, 3} when x > 0 -> IO.puts("x is positive: #{x}")
{1, x, 3} when x <= 0 -> IO.puts("x is not positive: #{x}")
_ -> IO.puts("No match")end5. with文でのパターンマッチング
Section titled “5. with文でのパターンマッチング”with文の基本
Section titled “with文の基本”def create_user(params) do with {:ok, user} <- validate_user(params), {:ok, saved_user} <- save_user(user), {:ok, _email} <- send_welcome_email(saved_user) do {:ok, saved_user} else {:error, reason} -> {:error, reason} endendネストしたパターンマッチング
Section titled “ネストしたパターンマッチング”def process_order(order_id) do with {:ok, order} <- fetch_order(order_id), {:ok, user} <- fetch_user(order.user_id), {:ok, payment} <- process_payment(order, user) do {:ok, %{order: order, user: user, payment: payment}} else {:error, :order_not_found} -> {:error, "Order not found"}
{:error, :user_not_found} -> {:error, "User not found"}
{:error, reason} -> {:error, "Payment failed: #{inspect(reason)}"} endend6. 実務でのベストプラクティス
Section titled “6. 実務でのベストプラクティス”パターン1: エラーハンドリング
Section titled “パターン1: エラーハンドリング”defmodule UserService do def create_user(params) do with {:ok, validated} <- validate_params(params), {:ok, user} <- insert_user(validated), {:ok, _} <- send_welcome_email(user) do {:ok, user} else {:error, %Ecto.Changeset{} = changeset} -> {:error, format_changeset_errors(changeset)}
{:error, reason} -> {:error, reason} end end
defp validate_params(params) do # バリデーションロジック if valid?(params) do {:ok, params} else {:error, :invalid_params} end endendパターン2: データ変換
Section titled “パターン2: データ変換”defmodule DataTransformer do def transform(%{status: :ok, data: data}) do {:ok, process_data(data)} end
def transform(%{status: :error, reason: reason}) do {:error, reason} end
def transform(data) when is_list(data) do Enum.map(data, &transform/1) end
def transform(_) do {:error, :invalid_format} endend7. よくある問題と解決策
Section titled “7. よくある問題と解決策”問題1: パターンがマッチしない
Section titled “問題1: パターンがマッチしない”原因:
- データ構造が期待と異なる
- ガード節の条件が厳しすぎる
解決策:
# デバッグ用のログを追加def process(data) do IO.inspect(data, label: "Received data")
case data do {:ok, value} -> handle_success(value) {:error, reason} -> handle_error(reason) other -> IO.inspect(other, label: "Unexpected pattern") endend問題2: 変数のシャドウイング
Section titled “問題2: 変数のシャドウイング”原因:
- 同じ変数名を複数回使用している
解決策:
# ピン演算子を使用x = 1{^x, y} = {1, 2} # xの値を固定
# または、異なる変数名を使用x = 1{_x, y} = {2, 3} # xを無視これで、パターンマッチングの基礎知識と実務での使い方を理解できるようになりました。