diff --git a/sigils.md b/sigils.md index e69de29..7b39c31 100644 --- a/sigils.md +++ b/sigils.md @@ -0,0 +1,188 @@ +--- +title: Сигилы +--- + +# Сигилы + +Мы уже знаем, что в Эликсирe есть строки с двойными кавычками и списки символов с одинарными кавычками. Однако, есть и другие структуры, у которых есть текстовое представление. Например, атомы, которые в основном создаются как `:atom`. + +Одна из целей Эликсира - быть расширяемым. Разработчики должны иметь возможность расширить язык таким образом, чтобы решить любую задачу. Компьютерные науки стали слишком широкой областью, чтобы ядро любого языка смогло вместить в себя все решения всех возможных проблем. Однако, если сделать язык расширяемым, то разработчики, компании и сообщества сами смогут добавить в него все необходимое. + +В текущей главе, мы рассмотрим сигилы - один из встроенных в язык механизмов для работы с текстовыми представлениями. Сигилы начинаются с символа тильда (`~`), после которого идет буква (которая идентифицирует сигил) и разделитель. Опционально, после разделителя могут быть добавлены еще и модификаторы. + +## Регулярные выражения + +Самый часто-используемый сигил в Эликсире - сигил [регулярных выражений](https://en.wikipedia.org/wiki/Regular_Expressions): `~r`. + +```elixir +# Регулярное выражение, которое находит в строки "foo" или "bar": +iex> regex = ~r/foo|bar/ +~r/foo|bar/ +iex> "foo" =~ regex +true +iex> "bat" =~ regex +false +``` + +Эликсир предоставляет совместимый с Перл синтаксис регулярных выражений, который реализован в библиотеке [PCRE](http://www.pcre.org/). Регулярные выражения поддерживают модификаторы. Например, модификатор `i` делает регулярное выражение нечувствительным к регистру: + +```elixir +iex> "HELLO" =~ ~r/hello/ +false +iex> "HELLO" =~ ~r/hello/i +true +``` + +В [модуле Regex](https://hexdocs.pm/elixir/Regex.html) доступно больше информации о других модификаторах и операциях с регулярными выражениями. + +Пока что во всех примерах мы использовали `/` для разделения сигилов. Однако, сигилы поддерживают 8 разных типов разделителей: + +```elixir +~r/hello/ +~r|hello| +~r"hello" +~r'hello' +~r(hello) +~r[hello] +~r{hello} +~r +``` + +Причина, по которой сигилы поддерживают разные типы разделителей, заключается в возможности писать код без экранирования. Регулярное выражение с прямыми слешами записанное в виде `~r(^https?://)` читается гораздо проще, чем такое же выражение в другой форме записи `~r/^https?:\/\//`. Аналогично, если в регулярном выражении есть прямые слеши и группы (которые используют `()`), то можно использовать двойные кавычки вместо слешей. + +## Строки, списки символов и сигил списка слов + +Кроме регулярных выражений в языке Эликсир есть три других сигила. + +### Строки + +Сигил `~s` используется для создания строк, также как и двойные кавычки. Сигил `~s` полезен, когда в самой строке есть двойные кавычки: + +```elixir +iex> ~s(this is a string with "double" quotes, not 'single' ones) +"this is a string with \"double\" quotes, not 'single' ones" +``` + +### Списки символов + +Сигил `~c` используется для создания списка символов, содержащих одинарную кавычку: + +```elixir +iex> ~c(this is a char list containing 'single quotes') +'this is a char list containing \'single quotes\'' +``` + +### Списки слов + +Сигил `~w` используется для создания списка слов (сами слова - обычные строки). Внутри сигила `~w` слова разделены пробелами: + +```elixir +iex> ~w(foo bar bat) +["foo", "bar", "bat"] +``` + +Сигил `~w` поддерживает модификаторы `c`, `s` и `a` (списки символов, строки и атомы соответственно) для указания типа данных членов итогового списка: + +```elixir +iex> ~w(foo bar bat)a +[:foo, :bar, :bat] +``` + +Кроме сигилов в нижнем регистре, Эликсир также поддерживает сигилы в верхнем регистре, чтобы работать с экранированием и интерполяцией. Оба сигила `~s` и `~S` вернут строку, с той разницей, что сигилы в верхнем регистре не поддерживают экранирование и интерполяцию: + +```elixir +iex> ~s(String with escape codes \x26 #{"inter" <> "polation"}) +"String with escape codes & interpolation" +iex> ~S(String without escape codes \x26 without #{interpolation}) +"String without escape codes \\x26 without \#{interpolation}" +``` + +Список поддерживаемых символов экранирования: + +- `\\` – одинарный обратный слеш +- `\a` – звонок/тревога +- `\b` – бекспейс +- `\d` - удаление +- `\e` - выход +- `\f` - форма подачи +- `\n` – новая строка +- `\r` – возврат каретки +- `\s` – пробел +- `\t` – таб +- `\v` – вертикальный таб +- `\0` - нулевой байт +- `\xDD` - представляет один байт в восьмеричной записи (например `\x13`) +- `\uDDDD` и `\u{D...}` - представляют символ Юникода (например `\u{1F600}`) + +В добавок, двойная кавычка внутри двойной кавычки должна быть экранирована как `\"`, аналогично и одинарная кавычка внутри одинарной кавычки `\'`. Тем не менее, лучше изменить разделители, чем использовать экранирование. + +Сигилы также поддерживают heredocs, в качестве разделителей используются три двойных или одинарных кавычки: + +```elixir +iex> ~s""" +...> this is +...> a heredoc string +...> """ +``` + +Чаще всего сигилы heredoc используют для написания документации. Например, если в документации встречается какой-то символ экранирования, то в документации могут появиться ошибки, потому что придется экранировать его дважды: + +```elixir +@doc """ +Converts double-quotes to single-quotes. + +## Examples + + iex> convert("\\\"foo\\\"") + "'foo'" + +""" +def convert(...) +``` + +Но если использовать сигил `~S`, то такой проблемы можно полностью избежать: + +```elixir +@doc ~S""" +Converts double-quotes to single-quotes. + +## Examples + + iex> convert("\"foo\"") + "'foo'" + +""" +def convert(...) +``` + +## Собственные сигилы + +Как уже было сказано в самом начале, сигилы - расширяемы. На самом деле использование сигила `~r/foo/i` равнозначно вызову `sigil_r` с двоичными данными и списком символов в качестве аргументов: + +```elixir +iex> sigil_r(<<"foo">>, 'i') +~r"foo"i +``` + +Мы можем обратиться к документации сигила `~r` через `sigil_r`: + +```elixir +iex> h sigil_r +... +``` + +Можно создавать собственные сигилы: нужно создать функцию, которая бы следовала формату `sigil_{имя_индентификатора}`. К примеру, давайте создадим сигил `~i`, который возвращает целое число (с опциональным модификатором `n`, чтобы сделать число отрицательным): + +```elixir +iex> defmodule MySigils do +...> def sigil_i(string, []), do: String.to_integer(string) +...> def sigil_i(string, [?n]), do: -String.to_integer(string) +...> end +iex> import MySigils +iex> ~i(13) +13 +iex> ~i(42)n +-42 +``` + +Сигилы могут быть использованы для выполнения работы на шаге компиляции. К примеру, регулярные выражения в Эликсире компилируются в производительные структуры, чтобы работать быстрее на шаге выполнения. Если вам интересен данный вопрос, то советуем изучить, как работают макросы, и посмотреть как реализованы сигилы в модуле `Kernel` (где реализованы все `sigil_*` функции).