Сопоставление с образцом

В этой главе мы покажем, что оператор = в Эликсире на самом деле оператор соответствия, и как использовать соответствие шаблону в структурах данных. В конце мы изучим пин-оператор ^ (от англ. pin – закреплять), который используется для доступа к ранее привязанным значениям.

Оператор соответствия

Мы уже пару раз использовали оператор = для присваивания переменных:

iex> x = 1
1

iex> x
1

На самом деле, оператор = в Эликсире является оператором соответствия. Давайте посмотрим почему:

iex> 1 = x
1

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

Обратите внимание, что 1 = x – корректное выражение, а оба значения равны 1 и соответствуют друг другу. Когда стороны не равны, выбрасывается ошибка MatchError.

Переменной может быть присвоено значение только при указании её слева от =:

iex> 1 = unknown
** (CompileError) iex:1: undefined function unknown/0

Т. к. переменная unknown не была ранее объявлена, Эликсир думает, что вы пытаетесь вызвать функцию unknown/0, но такой функции не существует.

Соответствие шаблону

Оператор соответствия используется не только для сопоставления простых значений, но также очень полезен для деструктуризации более сложных типов данных. Например, мы можем использовать соответствие шаблону для кортежей:

iex> {a, b, c} = {:hello, "world", 42}
{:hello, "world", 42}

iex> a
:hello

iex> b
"world"

Соответствие шаблону выбросит ошибку, если стороны не могут быть сопоставлены, например, если кортежи имеют разный размер:

iex> {a, b, c} = {:hello, "world"}
** (MatchError) no match of right hand side value: {:hello, "world"}

А также при сравнении разных типов:

iex> {a, b, c} = [:hello, "world", 42]
** (MatchError) no match of right hand side value: [:hello, "world", 42]

Более интересно то, что мы можем сопоставлять и определённые значения. В примере ниже левая часть будет соответствовать правой только в том случае, если кортеж будет начинаться с атома :ok:

iex> {:ok, result} = {:ok, 13}
{:ok, 13}

iex> result
13

iex> {:ok, result} = {:error, :oops}
** (MatchError) no match of right hand side value: {:error, :oops}

Мы можем сопоставлять списки:

iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]

iex> a
1

Список также поддерживает сопоставление с отделением головы и хвоста:

iex> [head | tail] = [1, 2, 3]
[1, 2, 3]
iex> head
1
iex> tail
[2, 3]

Аналогично функциям hd/1 и tl/1, мы не можем сопоставить пустой список с шаблоном голова/хвост:

iex> [h | t] = []
** (MatchError) no match of right hand side value: []

Формат [head | tail] используется не только для сопоставления, но и для добавления элементов в начало списка:

iex> list = [1, 2, 3]
[1, 2, 3]

iex> [0 | list]
[0, 1, 2, 3]

Соответствие шаблону позволяет разработчикам легко разбирать на составляющие такие типы данных, как кортежи и списки. Как мы увидим в следующих главах, это одна из основ рекурсии в Эликсире и отлично работает с другими типами, как, например, словари и двоичные данные.

Пин-оператор

Переменным в Эликсире могут быть присвоены новые значения:

iex> x = 1
1

iex> x = 2
2

Используйте пин-оператор ^, когда вы хотите сопоставить существующее значение переменной вместо привязки нового значения.

iex> x = 1
1

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

iex> {y, ^x} = {2, 1}
{2, 1}

iex> y
2

iex> {y, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}

Т. к. мы присвоили значение 1 переменной x, этот последний пример мог быть написан иначе:

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

Если переменная упоминается в шаблоне больше одного раза, все ссылки следует привязать к одному шаблону:

iex> {x, x} = {1, 1}
{1, 1}

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

В некоторых случаях вам не будет дела до какой-то части шаблона. В таком случае можно привязать такие значения к подчёркиванию _. Например, если нам нужна только голова списка, мы можем привязать хвост к подчеркиванию:

iex> [h | _] = [1, 2, 3]
[1, 2, 3]

iex> h
1

Переменная _ является особой в том смысле, что её никогда нельзя прочитать. Попытка сделать это даст ошибку:

iex> _
** (CompileError) iex:1: unbound variable _

Хотя соответствие шаблону позволяет нам строить мощные конструкции, его использование ограничено. Например, нельзя сделать вызов функции в левой стороне сопоставления. Следующий пример не сработает:

iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: illegal pattern

На этом наше введение в соответствие шаблону заканчивается. Как мы увидим в следующей главе, сопоставление очень распространено во многих конструкциях языка.

© 2020 / Россия Любые мысли и вопросы пишите на elixir@wunsh.ru.