Spec-Zone.ru › Elixir 1.13

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

Базовый доступ к данным через ключи.

Модуль 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(данные, текущее_значение)
any_container()
контейнер()
get_and_update_fun(данные, текущее_значение)
get_fun(данные)
ключ()
nil_container()
t()
значение()

Обработчики

fetch(элемент, ключ)

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

get_and_update(данные, ключ, функция)

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

pop(данные, ключ)

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

Функции

all()

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

at!(индекс)

То же, что и at/1, за исключением того, что оно поднимает исключение Enum.OutOfBoundsError, если заданный индекс вне диапазона.

at(индекс)

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

elem(индекс)

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

fetch!(контейнер, ключ)

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

fetch(контейнер, ключ)

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

filter(функция)

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

get(контейнер, ключ, значение_по_умолчанию \\ nil)

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

get_and_update(контейнер, ключ, функция)

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

key!(ключ)

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

key(ключ, значение_по_умолчанию \\ nil)

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

pop(контейнер, ключ)

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

END_OF_DOCUMENT_MARKER

Типы

access_fun(data, current_value)Source

@type access_fun(data, current_value) ::
  get_fun(data) | get_and_update_fun(data, current_value)

any_container()Source

@type any_container() :: any()

container()Source

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

get_and_update_fun(data, current_value)Source

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

get_fun(data)Source

@type get_fun(data) :: (:get, data, (term() -> term()) -> new_data :: container())

key()Source

@type key() :: any()

nil_container()Source

@type nil_container() :: nil

t()Source

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

value()Source

@type value() :: any()

Обработчики

fetch(term, key)Source

@callback 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)Source

@callback get_and_update(
  data,
  key(),
  (value() | nil -> {current_value, new_value :: value()} | :pop)
) ::
  {current_value, new_data :: data}
when current_value: value(), data: container() | any_container()

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

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

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

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

  • new_value — новое значение, которое должно быть сохранено по ключу key

  • new_data — data после обновления значения по ключу key значением new_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)Source

@callback 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 для примеров.

END_OF_DOCUMENT_MARKER

Функции

all()Source

@spec all() :: access_fun(data :: list(), current_value :: list())

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

Возвращаемая функция обычно передаётся в качестве доступа к 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)Source

@spec at!(integer()) :: access_fun(data :: list(), current_value :: term())

То же самое, что и at/1, за исключением того, что она вызывает Enum.OutOfBoundsError, если указанный индекс находится вне границ.

Примеры

iex> get_in([:a, :b, :c], [Access.at!(2)])
:c
iex> get_in([:a, :b, :c], [Access.at!(3)])
** (Enum.OutOfBoundsError) out of bounds error

at(index)Source

@spec at(integer()) :: access_fun(data :: list(), current_value :: term())

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

Возвращаемая функция обычно передаётся в качестве доступа к 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)Source

@spec elem(non_neg_integer()) :: access_fun(data :: tuple(), current_value :: term())

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

Возвращаемая функция обычно передаётся в качестве доступа к 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)Source

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

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

Примеры

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

fetch(container, key)Source

@spec fetch(container(), term()) :: {:ok, term()} | :error
@spec 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

filter(func)Source

@spec filter((term() -> boolean())) ::
  access_fun(data :: list(), current_value :: list())

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

Возвращаемая функция обычно передаётся в качестве доступа к 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)Source

@spec get(container(), term(), term()) :: term()
@spec 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)Source

@spec get_and_update(
  data,
  key(),
  (value() | nil -> {current_value, new_value :: value()} | :pop)
) ::
  {current_value, new_data :: data}
when data: container(), current_value: var

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

Аргумент fun получает значение key (или nil, если key отсутствует в container и должен вернуть кортеж из двух элементов {current_value, new_value}: значение «get» current_value (извлеченное значение, которое можно обработать перед возвратом) и новое значение, которое должно быть сохранено по key (new_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)Source

@spec key!(key()) :: access_fun(data :: struct() | map(), current_value :: term())

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

Возвращаемая функция обычно передаётся в качестве доступа к 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: []

key(key, default \\ nil)Source

@spec key(key(), term()) ::
  access_fun(data :: struct() | map(), current_value :: term())

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

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

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

iex> get_in(%{}, [Access.key(:user, %{}), Access.key(:name, "meg")])
"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([], [Access.key(:foo)])
** (BadMapError) expected a map, got: []

pop(container, key)Source

@spec 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"}}

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

Spec-Zone.ru

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