Исходный код Сопоставление с образцом
В этой главе мы узнаем, почему оператор = в Elixir называется оператором сопоставления и как использовать его для сопоставления с образцом внутри структур данных. Мы узнаем об операторе привязки ^, используемом для доступа к ранее связанным значениям.
Оператор сопоставления
Мы несколько раз использовали оператор = для присвоения переменных в Elixir:
iex> x = 1 1 iex> x 1
В Elixir оператор = фактически называется оператором сопоставления. Давайте посмотрим почему:
iex> x = 1 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 variable "unknown"
Сопоставление с образцом
Оператор сопоставления используется не только для сопоставления со значениями, но также полезен для деструктуризации более сложных типов данных. Например, мы можем сопоставлять с образцом кортежи:
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 и tl, пустой список нельзя сопоставить с образцом головы и хвоста:
iex> [head | tail] = [] ** (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]
Сопоставление с образцом позволяет разработчикам легко деструктурировать такие типы данных, как кортежи и списки. Как мы увидим в следующих главах, это один из оснований рекурсии в Elixir и применяется также к другим типам, таким как карты и бинарные данные.
Оператор привязки
Переменные в Elixir могут быть перепривязаны:
iex> x = 1 1 iex> x = 2 2
Однако бывают случаи, когда мы не хотим перепривязывать переменные.
Используйте оператор привязки ^, когда нужно сопоставить с образцом существующего значения переменной, а не перепривязывать её.
iex> x = 1 1 iex> ^x = 2 ** (MatchError) no match of right hand side value: 2
Поскольку мы привязали x, когда оно было связано со значением 1, это эквивалентно следующему:
iex> 1 = 2 ** (MatchError) no match of right hand side value: 2
Обратите внимание, что мы видим даже то же сообщение об ошибке.
Мы можем использовать оператор привязки внутри других сопоставлений с образцом, таких как кортежи или списки:
iex> x = 1
1
iex> [^x, 2, 3] = [1, 2, 3]
[1, 2, 3]
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}
Поскольку x было привязано к значению 1 при привязке, этот последний пример можно было бы записать как:
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> [head | _] = [1, 2, 3] [1, 2, 3] iex> head 1
Переменная _ является специальной, так как из неё нельзя читать. Попытка прочитать из неё приведёт к ошибке компиляции:
iex> _ ** (CompileError) iex:1: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
Хотя сопоставление с образцом позволяет создавать мощные конструкции, его использование ограничено. Например, нельзя выполнять вызовы функций слева от сопоставления. Следующий пример некорректен:
iex> length([1, [2], 3]) = 3 ** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match
Это завершает наше введение в сопоставление с образцом. Как мы увидим в следующей главе, сопоставление с образцом очень распространено во многих языковых конструкциях и может быть дополнено охранными условиями.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/pattern-matching.html