Spec-Zone.ru › Elixir 1.10

Доступ поведение

Доступ к структурам данных на основе ключей.

Модуль Access определяет поведение для динамического доступа к ключам любого типа в структуре данных с помощью синтаксиса data[key].

Access поддерживает списки ключевых слов (Keyword) и карты (Map) «из коробки». Ключ может быть любого типа, и он возвращает nil, если ключ не существует:

iex> keywords = [a: 1, b: 2]
iex> keywords[:a]
1
iex> keywords[:c]
nil

iex> map = %{a: 1, b: 2}
iex> map[:a]
1

iex> star_ratings = %{1.0 => "★", 1.5 => "★☆", 2.0 => "★★"}
iex> star_ratings[1.5]
"★☆"

Этот синтаксис очень удобен, так как он может быть вложен произвольно:

iex> keywords = [a: 1, b: 2]
iex> keywords[:c][:unknown]
nil

Это работает, потому что доступ к чему-либо в значении nil возвращает само значение nil:

iex> nil[:a]
nil

Синтаксис доступа также может быть использован с макросами Kernel.put_in/2, Kernel.update_in/2 и Kernel.get_and_update_in/2, чтобы разрешить установку значений вложенных структурах данных:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

Внимание! Хотя синтаксис доступа разрешен в картах через map[key], если ваша карта состоит из предопределённых атомных ключей, вы должны предпочесть доступ к этим атомным ключам с помощью map.key вместо map[key], так как map.key будет поднято, если ключ отсутствует. Это важно, потому что, если в карте есть предопределённый набор ключей и ключ отсутствует, это, скорее всего, ошибка в вашем программном обеспечении или опечатка в имени ключа. По этой причине, поскольку структуры предопределены по своей природе, они допускают только синтаксис struct.key и не допускают синтаксис доступа struct[key]. Подробнее см. модуль Map.

Вложенные структуры данных

Оба синтаксиса доступа на основе ключей могут быть использованы с функциями и макросами вложенного обновления в Kernel, таких как Kernel.get_in/2, Kernel.put_in/3, Kernel.update_in/3, Kernel.pop_in/2 и Kernel.get_and_update_in/3.

Например, чтобы обновить карту внутри другой карты:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

Этот модуль предоставляет удобные функции для обхода других структур, таких как кортежи и списки. Эти функции могут быть использованы во всех функциях и макросах, связанных с Access в Kernel.

Например, задана карта пользователя с ключами :name и :languages, вот как можно глубоко пройтись по карте и преобразовать все имена языков в верхний регистр:

iex> languages = [
...>   %{name: "elixir", type: :functional},
...>   %{name: "c", type: :procedural}
...> ]
iex> user = %{name: "john", languages: languages}
iex> update_in(user, [:languages, Access.all(), :name], &String.upcase/1)
%{
  name: "john",
  languages: [
    %{name: "ELIXIR", type: :functional},
    %{name: "C", type: :procedural}
  ]
}

См. функции key/1, key!/1, elem/1 и all/0 для некоторых доступных селекторов.

Краткое описание

Типы

access_fun(data, get_value)
any_container()
container()
get_and_update_fun(data, get_value)
get_fun(data, get_value)
key()
nil_container()
t()
value()

Функции

all()

Возвращает функцию, которая получает доступ ко всем элементам в списке.

at(index)

Возвращает функцию, которая получает доступ к элементу по index (нумерация с нуля) в списке.

elem(index)

Возвращает функцию, которая получает доступ к элементу по заданному индексу в кортеже.

fetch(container, key)

Извлекает значение для заданного ключа в контейнере (карта, список ключевых слов или структура, реализующая поведение Access).

fetch!(container, key)

То же, что и fetch/2, но возвращает значение напрямую или поднимает исключение KeyError, если key не найден.

filter(func)

Возвращает функцию, которая получает доступ ко всем элементам списка, которые соответствуют заданному предикату.

get(container, key, default \\ nil)

Получает значение для данного ключа в контейнере (карта, список ключевых слов или структура, реализующая поведение Access).

get_and_update(container, key, fun)

Получает и обновляет заданный ключ в container (карта, список ключевых слов, структура, реализующая поведение Access).

key(key, default \\ nil)

Возвращает функцию, которая получает доступ к заданному ключу в карте/структуре.

key!(key)

Возвращает функцию, которая получает доступ к заданному ключу в карте/структуре.

pop(container, key)

Удаляет запись с заданным ключом из контейнера (карта, список ключевых слов или структура, реализующая поведение Access).

Обработчики событий

fetch(term, key)

Вызывается для доступа к значению, хранящемуся по ключу key в заданном выражении term.

get_and_update(data, key, function)

Вызывается для доступа к значению по ключу key и одновременного обновления.

pop(data, key)

Вызывается для "извлечения" значения по ключу key из заданной структуры данных.

Типы

access_fun(data, get_value)

Спецификации

access_fun(data, get_value) ::
  get_fun(data, get_value) | get_and_update_fun(data, get_value)

any_container()

Спецификации

any_container() :: any()

container()

Спецификации

container() :: keyword() | struct() | map()

get_and_update_fun(data, get_value)

Спецификации

get_and_update_fun(data, get_value) ::
  (:get_and_update, data, (term() -> term()) ->
     {get_value, new_data :: container()} | :pop)

get_fun(data, get_value)

Спецификации

get_fun(data, get_value) ::
  (:get, data, (term() -> term()) -> {get_value, new_data :: container()})

key()

Спецификации

key() :: any()

nil_container()

Спецификации

nil_container() :: nil

t()

Спецификации

t() :: container() | nil_container() | any_container()

value()

Спецификации

value() :: any()

Функции

all()

Спецификации

all() :: access_fun(data :: list(), get_value :: list())

Возвращает функцию, которая обращается ко всем элементам в списке.

Возвращаемая функция обычно передается как accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Примеры

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.all(), :name])
["john", "mary"]
iex> get_and_update_in(list, [Access.all(), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["john", "mary"], [%{name: "JOHN"}, %{name: "MARY"}]}
iex> pop_in(list, [Access.all(), :name])
{["john", "mary"], [%{}, %{}]}

Вот пример, который проходит по списку, отбрасывая четные числа и умножая нечетные на 2:

iex> require Integer
iex> get_and_update_in([1, 2, 3, 4, 5], [Access.all()], fn num ->
...>   if Integer.is_even(num), do: :pop, else: {num, num * 2}
...> end)
{[1, 2, 3, 4, 5], [2, 6, 10]}

Ошибка возникает, если обращается к структуре, которая не является списком:

iex> get_in(%{}, [Access.all()])
** (RuntimeError) Access.all/0 expected a list, got: %{}

at(index)

Спецификации

at(integer()) :: access_fun(data :: list(), get_value :: term())

Возвращает функцию, которая обращается к элементу по индексу index (нумерация с нуля) в списке.

Возвращаемая функция обычно передается как accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Примеры

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.at(1), :name])
"mary"
iex> get_in(list, [Access.at(-1), :name])
"mary"
iex> get_and_update_in(list, [Access.at(0), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", [%{name: "JOHN"}, %{name: "mary"}]}
iex> get_and_update_in(list, [Access.at(-1), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"mary", [%{name: "john"}, %{name: "MARY"}]}

at/1 также можно использовать для извлечения элементов из списка или ключей внутри списка:

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> pop_in(list, [Access.at(0)])
{%{name: "john"}, [%{name: "mary"}]}
iex> pop_in(list, [Access.at(0), :name])
{"john", [%{}, %{name: "mary"}]}

Если индекс выходит за пределы границ, возвращается nil, и функция обновления никогда не вызывается:

iex> list = [%{name: "john"}, %{name: "mary"}]
iex> get_in(list, [Access.at(10), :name])
nil
iex> get_and_update_in(list, [Access.at(10), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{nil, [%{name: "john"}, %{name: "mary"}]}

Ошибка возникает, если обращается к структуре, которая не является списком:

iex> get_in(%{}, [Access.at(1)])
** (RuntimeError) Access.at/1 expected a list, got: %{}

elem(index)

Спецификации

elem(non_neg_integer()) :: access_fun(data :: tuple(), get_value :: term())

Возвращает функцию, которая обращается к элементу по заданному индексу в кортеже.

Возвращаемая функция обычно передается как accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Возвращаемая функция генерирует ошибку, если index выходит за пределы границ.

Обратите внимание, что извлечение элементов из кортежей невозможно и приводит к ошибке.

Примеры

iex> map = %{user: {"john", 27}}
iex> get_in(map, [:user, Access.elem(0)])
"john"
iex> get_and_update_in(map, [:user, Access.elem(0)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: {"JOHN", 27}}}
iex> pop_in(map, [:user, Access.elem(0)])
** (RuntimeError) cannot pop data from a tuple

Ошибка возникает, если обращается к структуре, которая не является кортежем:

iex> get_in(%{}, [Access.elem(0)])
** (RuntimeError) Access.elem/1 expected a tuple, got: %{}

fetch(container, key)

Спецификации

fetch(container(), term()) :: {:ok, term()} | :error
fetch(nil_container(), any()) :: :error

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

Возвращает {:ok, value}, где value - значение по ключу key, если такой ключ существует, или :error, если ключ key не найден.

Примеры

iex> Access.fetch(%{name: "meg", age: 26}, :name)
{:ok, "meg"}

iex> Access.fetch([ordered: true, on_timeout: :exit], :timeout)
:error

fetch!(container, key)

Спецификации

fetch!(container(), term()) :: term()

То же, что и fetch/2, но возвращает значение непосредственно или генерирует исключение KeyError, если key не найден.

Примеры

iex> Access.fetch!(%{name: "meg", age: 26}, :name)
"meg"

filter(func)

Спецификации

filter((term() -> boolean())) :: access_fun(data :: list(), get_value :: list())

Возвращает функцию, которая обращается ко всем элементам списка, соответствующим заданному предикату.

Возвращаемая функция обычно передается как accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Примеры

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> get_in(list, [Access.filter(&(&1.salary > 20)), :name])
["francine"]
iex> get_and_update_in(list, [Access.filter(&(&1.salary <= 20)), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["john"], [%{name: "JOHN", salary: 10}, %{name: "francine", salary: 30}]}

filter/1 также можно использовать для извлечения элементов из списка или ключей внутри списка:

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> pop_in(list, [Access.filter(&(&1.salary >= 20))])
{[%{name: "francine", salary: 30}], [%{name: "john", salary: 10}]}
iex> pop_in(list, [Access.filter(&(&1.salary >= 20)), :name])
{["francine"], [%{name: "john", salary: 10}, %{salary: 30}]}

Если совпадений не найдено, возвращается пустой список, и функция обновления никогда не вызывается

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]
iex> get_in(list, [Access.filter(&(&1.salary >= 50)), :name])
[]
iex> get_and_update_in(list, [Access.filter(&(&1.salary >= 50)), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{[], [%{name: "john", salary: 10}, %{name: "francine", salary: 30}]}

Ошибка возникает, если предикат не является функцией или имеет неправильную арность:

iex> get_in([], [Access.filter(5)])
** (FunctionClauseError) no function clause matching in Access.filter/1

Ошибка возникает, если обращается к структуре, которая не является списком:

iex> get_in(%{}, [Access.filter(fn a -> a == 10 end)])
** (RuntimeError) Access.filter/1 expected a list, got: %{}

get(container, key, default \\ nil)

Спецификации

get(container(), term(), term()) :: term()
get(nil_container(), any(), default) :: default when default: var

Получает значение для заданного ключа в контейнере (словарь, список ключевых слов или структура, реализующая поведение Access).

Возвращает значение по ключу key, если такой ключ существует, или default, если ключ key не найден.

Примеры

iex> Access.get(%{name: "john"}, :name, "default name")
"john"
iex> Access.get(%{name: "john"}, :age, 25)
25

iex> Access.get([ordered: true], :timeout)
nil

get_and_update(container, key, fun)

Спецификации

get_and_update(data, key(), (value() -> {get_value, value()} | :pop)) ::
  {get_value, data}
when data: container(), get_value: var

Получает и обновляет заданный ключ в контейнере (словарь, список ключевых слов, структура, реализующая поведение Access).

Аргумент fun получает значение ключа key (или nil, если ключ key отсутствует в container) и должен вернуть кортеж из двух элементов {get_value, update_value}: значение "get" get_value (полученное значение, которое можно обработать перед возвращением) и новое значение, которое должно быть сохранено по ключу key (update_value). fun также может вернуть :pop, что означает, что текущее значение должно быть удалено из контейнера и возвращено.

Возвращаемое значение — кортеж из двух элементов: значение "get", возвращенное fun, и новый контейнер с обновленным значением по ключу key.

Примеры

iex> Access.get_and_update([a: 1], :a, fn current_value ->
...>   {current_value, current_value + 1}
...> end)
{1, [a: 2]}

key(key, default \\ nil)

Спецификации

key(key(), term()) :: access_fun(data :: struct() | map(), get_value :: term())

Возвращает функцию, которая обращается к заданному ключу в словаре/структуре.

Возвращаемая функция обычно передается в качестве accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Возвращаемая функция использует значение по умолчанию, если ключ не существует. Это позволяет задавать значения по умолчанию и безопасно проходить по отсутствующим ключам:

iex> get_in(%{}, [Access.key(:user, %{name: "meg"}), Access.key(:name)])
"meg"

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

iex> put_in(%{}, [Access.key(:user, %{}), Access.key(:name)], "Mary")
%{user: %{name: "Mary"}}

Примеры

iex> map = %{user: %{name: "john"}}
iex> get_in(map, [Access.key(:unknown, %{}), Access.key(:name, "john")])
"john"
iex> get_and_update_in(map, [Access.key(:user), Access.key(:name)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}
iex> pop_in(map, [Access.key(:user), Access.key(:name)])
{"john", %{user: %{}}}

Ошибка возникает, если обращается к структуре, которая не является словарем или структурой:

iex> get_in(nil, [Access.key(:foo)])
** (BadMapError) expected a map, got: nil

iex> get_in([], [Access.key(:foo)])
** (BadMapError) expected a map, got: []

key!(key)

Спецификации

key!(key()) :: access_fun(data :: struct() | map(), get_value :: term())

Возвращает функцию, которая обращается к заданному ключу в словаре/структуре.

Возвращаемая функция обычно передается в качестве accessor к Kernel.get_in/2, Kernel.get_and_update_in/3 и подобным функциям.

Аналогично key/2, но возвращаемая функция генерирует ошибку, если ключ не существует.

Примеры

iex> map = %{user: %{name: "john"}}
iex> get_in(map, [Access.key!(:user), Access.key!(:name)])
"john"
iex> get_and_update_in(map, [Access.key!(:user), Access.key!(:name)], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}
iex> pop_in(map, [Access.key!(:user), Access.key!(:name)])
{"john", %{user: %{}}}
iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)])
** (KeyError) key :unknown not found in: %{name: "john"}

Ошибка возникает, если обращается к структуре, которая не является словарем/структурой:

iex> get_in([], [Access.key!(:foo)])
** (RuntimeError) Access.key!/1 expected a map/struct, got: []

pop(container, key)

Спецификации

pop(data, key()) :: {value(), data} when data: container()

Удаляет запись с заданным ключом из контейнера (словарь, список ключевых слов или структура, реализующая поведение Access).

Возвращает кортеж, содержащий значение, связанное с ключом, и обновленный контейнер. nil возвращается для значения, если ключ не находится в контейнере.

Примеры

Со словарем:

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :name)
{"Elixir", %{creator: "Valim"}}

Список ключевых слов:

iex> Access.pop([name: "Elixir", creator: "Valim"], :name)
{"Elixir", [creator: "Valim"]}

Неизвестный ключ:

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :year)
{nil, %{creator: "Valim", name: "Elixir"}}

Обработчики событий

fetch(term, key)

Характеристики

fetch(term :: t(), key()) :: {:ok, value()} | :error

Вызывается для доступа к значению, хранящемуся под key в заданном элементе term.

Эта функция должна вернуть {:ok, value}, где value — значение под key, если ключ существует в элементе, или :error, если ключ отсутствует в элементе.

Многие функции, определенные в модуле Access, внутренне вызывают эту функцию. Эта функция также используется при использовании синтаксиса доступа с квадратными скобками (structure[key]): вызывается обработчик fetch/2, реализованный модулем, определяющим структуру structure, и если он возвращает {:ok, value}, то возвращается value, а если он возвращает :error, то возвращается nil.

См. реализации Map.fetch/2 и Keyword.fetch/2 для примеров реализации этого обработчика.

get_and_update(data, key, function)

Характеристики

get_and_update(data, key(), (value() -> {get_value, value()} | :pop)) ::
  {get_value, data}
when data: container() | any_container(), get_value: var

Вызывается для доступа к значению под key и одновременного его обновления.

Реализация этого обработчика должна вызвать fun со значением под key в переданной структуре data или с nil, если key в ней отсутствует. Эта функция должна вернуть либо {get_value, update_value}, либо :pop.

Если переданная функция возвращает {get_value, update_value}, значение возвращаемое этим обработчиком должно быть {get_value, new_data}, где:

  • get_value — извлеченное значение (которое можно обработать перед возвратом)

  • update_value — новое значение, которое нужно сохранить под key

  • new_data — data после обновления значения key с помощью update_value.

Если переданная функция возвращает :pop, значение, возвращаемое этим обработчиком, должно быть {value, new_data}, где value — значение под key (или nil, если оно отсутствует), а new_data — data без key.

См. реализации Map.get_and_update/3 или Keyword.get_and_update/3 для получения дополнительных примеров.

pop(data, key)

Характеристики

pop(data, key()) :: {value(), data} when data: container() | any_container()

Вызывается для удаления значения под key из заданной структуры данных.

Если key существует в заданной структуре данных data, реализация должна вернуть кортеж {value, new_data}, где value — значение, которое было под key, а new_data — term без key.

Если key отсутствует в заданной структуре, должен быть возвращен кортеж {value, data}, где value определено реализацией.

См. реализации Map.pop/3 или Keyword.pop/3 для получения дополнительных примеров.

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.10.4/Access.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API