Операторы
В данном документе рассматриваются операторы в Elixir, как они парсятся, как могут быть определены и как могут быть переопределены.
Приоритет и ассоциативность операторов
Ниже приведён список всех операторов, которые может парсить Elixir, упорядоченные по убыванию приоритета вместе с их ассоциативностью:
| Оператор | Ассоциативность |
|---|---|
@ |
Унарный |
. |
Слева направо |
+ - ! ^ not ~~~
|
Унарный |
* /
|
Слева направо |
+ -
|
Слева направо |
++ -- .. <>
|
Справа налево |
^^^ |
Слева направо |
in not in
|
Слева направо |
|> <<< >>> <<~ ~>> <~ ~> <~> <|>
|
Слева направо |
< > <= >=
|
Слева направо |
== != =~ === !==
|
Слева направо |
&& &&& and
|
Слева направо |
|| ||| or
|
Слева направо |
= |
Справа налево |
& |
Унарный |
=> (допустимый синтаксис только внутри %{}) |
Справа налево |
| |
Справа налево |
:: |
Справа налево |
when |
Справа налево |
<- \\
|
Слева направо |
Операторы сравнения
Elixir предоставляет следующие встроенные операторы сравнения:
-
==- равенство -
===- строгое равенство -
!=- неравенство -
!==- строгое неравенство -
<- меньше -
>- больше -
<=- меньше или равно -
>=- больше или равно
Единственное различие между == и === заключается в том, что === является строгим при сравнении целых чисел и чисел с плавающей точкой:
iex> 1 == 1.0 true iex> 1 === 1.0 false
!= и !== действуют как отрицание == и === соответственно.
Порядок терминов
В Elixir разные типы данных могут сравниваться с помощью операторов сравнения:
iex> 1 < :an_atom true
Причина, по которой мы можем сравнивать разные типы данных, заключается в прагматизме. Алгоритмы сортировки не должны беспокоиться о разных типах данных для сортировки. Для справки, общий порядок сортировки определён ниже:
number < atom < reference < function < port < pid < tuple < map < list < bitstring
При сравнении двух чисел разных типов (число — это целое число или число с плавающей точкой), всегда происходит преобразование в тип с большей точностью, если используемый оператор сравнения не === или !==. Число с плавающей точкой будет считаться более точным, чем целое число, за исключением случаев, когда число с плавающей точкой больше/меньше +/-9007199254740992.0 соответственно, в этом случае все значащие цифры числа с плавающей точкой находятся слева от десятичной точки. Такое поведение существует для того, чтобы сравнение больших чисел оставалось транзитивным.
Типы коллекций сравниваются по следующим правилам:
- Кортежи сравниваются по размеру, а затем по элементам по очереди.
- Карты сравниваются по размеру, затем по ключам в порядке возрастания порядка терминов, затем по значениям в порядке ключей. В частности, в порядке упорядочивания ключей карты целые числа всегда считаются меньше чисел с плавающей точкой.
- Списки сравниваются по элементам по очереди.
- Битовые строки сравниваются по байту, неполные байты сравниваются по биту.
Пользовательские и переопределенные операторы
Определение пользовательских операторов
Elixir может парсить предопределенный набор операторов; это означает, что невозможно определить новые операторы (в отличие от Haskell, например). Однако не все операторы, которые может парсить Elixir, используются Elixir: например, + и || используются Elixir для сложения и логического или, но <~> не используется (но допустим).
Для определения оператора можно использовать обычные конструкции def* (def, defp, defmacro, и так далее), но с синтаксисом, аналогичным тому, как используется оператор:
defmodule MyOperators do # We define ~> to return the maximum of the given two numbers, # and <~ to return the minimum. def a ~> b, do: max(a, b) def a <~ b, do: min(a, b) end
Для использования новых операторов, мы должны импортировать модуль, который их определяет:
iex> import MyOperators iex> 1 ~> 2 2 iex> 1 <~ 2 1
Ниже приведена таблица всех операторов, которые Elixir может парсить, но которые по умолчанию не используются:
-
| -
||| -
&&& -
<<< -
>>> -
<<~ -
~>> -
<~ -
~> -
<~> -
<|> -
^^^ -
~~~
Следующие операторы используются модулем Bitwise при импорте: &&&, ^^^, <<<, >>>, |||, ~~~. Дополнительную информацию см. в документации для Bitwise.
Переопределение существующих операторов
Операторы, которые использует Elixir (например, +) могут быть определены любым модулем и использоваться вместо определенных Elixir, при условии, что они не импортированы из Kernel (который импортируется по умолчанию во всех местах). Например:
defmodule WrongMath do # Let's make math wrong by changing the meaning of +: def a + b, do: a - b end
Теперь, если мы попытаемся использовать этот оператор «из коробки», мы получим ошибку:
iex> import WrongMath iex> 1 + 2 ** (CompileError) iex:11: function +/2 imported from both WrongMath and Kernel, call is ambiguous
Поэтому, как упоминалось выше, мы должны явно не импортировать +/2 из Kernel:
iex> import WrongMath iex> import Kernel, except: [+: 2] iex> 1 + 2 -1
Заключительная заметка
Хотя возможно определить неиспользуемые операторы (например, <~>) и «переопределить» предопределенные операторы (например, +), сообщество Elixir в целом не рекомендует это делать. Пользовательские операторы могут быть очень сложными для чтения и ещё сложнее для понимания, так как они не имеют описательного имени, как функции. Тем не менее, в некоторых конкретных случаях или в языках предметной области (DSL) эти практики могут быть оправданы.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.9.4/operators.html