Зіставлення по зразку (pattern matching) — це одна з найпотужніших можливостей мови Elixir. Це не просто присвоєння значень змінним, а механізм для деструктуризації даних та перевірки їхньої структури.
Основи зіставлення
В Elixir оператор = — це не оператор присвоєння, а оператор зіставлення. Він намагається зробити ліву частину рівною правій.
# Проста змінна
x = 1
# x тепер дорівнює 1
# Зіставлення зі значенням
1 = x
# Це працює, тому що x = 1
# Помилка зіставлення
2 = x
# ** (MatchError) no match of right hand side value: 1
Зіставлення зі списками
Зіставлення дозволяє витягувати елементи зі списків:
# Зіставлення всього списку
[a, b, c] = [1, 2, 3]
# a = 1, b = 2, c = 3
# Виділення голови та хвоста
[head | tail] = [1, 2, 3, 4]
# head = 1, tail = [2, 3, 4]
# Ігнорування елементів
[_, second, _] = [10, 20, 30]
# second = 20
Зіставлення з кортежами
Кортежі часто використовуються для зіставлення, особливо для обробки результатів функцій:
# Зіставлення з кортежем
{status, result} = {:ok, "Success"}
# status = :ok, result = "Success"
# Обробка помилок
case File.read("file.txt") do
{:ok, content} ->
IO.puts("Вміст: #{content}")
{:error, reason} ->
IO.puts("Помилка: #{reason}")
end
Зіставлення з картами (maps)
Карти дозволяють витягувати значення за ключами:
# Витягування значень
%{name: name, age: age} = %{name: "Іван", age: 30, city: "Київ"}
# name = "Іван", age = 30
# Часткове зіставлення
%{name: name} = %{name: "Марія", age: 25}
# name = "Марія" (age ігнорується)
# Зіставлення з конкретним значенням
%{status: :active, user: user} = %{status: :active, user: "admin"}
# Спрацює тільки якщо status = :active
Пін-оператор (^)
Використовується для зіставлення з існуючим значенням змінної замість створення нової прив'язки:
x = 1
# Без пін-оператора (перезапис)
x = 2
# x тепер дорівнює 2
# З пін-оператором (зіставлення)
x = 1
^x = 1 # OK
^x = 2 # MatchError
# Практичне застосування
[^x, y] = [1, 2] # OK, y = 2
[^x, y] = [2, 3] # MatchError
Зіставлення у функціях
Зіставлення по зразку особливо корисне при визначенні функцій з різними варіантами вхідних даних:
defmodule Calculator do
# Зіставлення за значенням
def operate(:add, a, b), do: a + b
def operate(:subtract, a, b), do: a - b
def operate(:multiply, a, b), do: a * b
# Зіставлення зі структурами
def process_user(%{name: name, admin: true}) do
"Адміністратор: #{name}"
end
def process_user(%{name: name}) do
"Користувач: #{name}"
end
# Рекурсія зі списками
def sum([]), do: 0
def sum([head | tail]), do: head + sum(tail)
end
Охоронці (Guards)
Охоронці дозволяють додавати додаткові умови до зіставлення:
defmodule Numbers do
def check(x) when x > 0, do: "Позитивне"
def check(x) when x < 0, do: "Негативне"
def check(0), do: "Нуль"
def adult?(age) when age >= 18, do: true
def adult?(_), do: false
# Комбіновані охоронці
def classify(x) when is_integer(x) and x > 0, do: "Натуральне"
def classify(x) when is_float(x), do: "Дробове"
end
Практичні приклади
Приклади реального використання зіставлення:
# Обробка HTTP-відповідей
case HTTPoison.get(url) do
{:ok, %{status_code: 200, body: body}} ->
{:ok, body}
{:ok, %{status_code: 404}} ->
{:error, :not_found}
{:error, reason} ->
{:error, reason}
end
# Парсинг команд
def handle_command(["create", name]) do
create_item(name)
end
def handle_command(["delete", id]) when is_binary(id) do
delete_item(id)
end
def handle_command(_) do
{:error, "Невідома команда"}
end
Переваги зіставлення по зразку
- Читабельність: код стає більш декларативним і зрозумілим
- Безпека: компілятор попереджає про неповні зіставлення
- Деструктуризація: легко витягувати дані зі складних структур
- Елегантність: замінює багато if/else конструкцій
- Функціональний стиль: природно поєднується з рекурсією та незмінністю
Зіставлення по зразку — це фундаментальна концепція Elixir, яка робить код більш виразним та надійним.
Коментарі
Дописати коментар