Spec-Zone.ru › Elixir 1.14

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

Базирующий доступ к структурам данных.

Модуль 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, current_value)
container()
get_and_update_fun(data, current_value)
get_fun(data)
key()
nil_container()
t()
value()

Обработчики

fetch(term, key)

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

get_and_update(data, key, function)

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

pop(data, key)

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

Функции

all()

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

at(index)

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

at!(index)

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

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).

slice(range)

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

Типы

access_fun(data, current_value)Source

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

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()

value()Source

@type value() :: any()
END_OF_DOCUMENT_MARKER

Обратные вызовы

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()

Вызывается для доступа к значению по ключу 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()

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

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

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

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

Функции

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(индекс)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: %{}

at!(индекс)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

elem(индекс)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(контейнер, ключ)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

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

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

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

Примеры

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

filter(функция)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(контейнер, ключ, значение_по_умолчанию \\ 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(контейнер, ключ, функция)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(ключ, значение_по_умолчанию \\ 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: []

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: []

pop(контейнер, ключ)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"}}

slice(диапазон)Источник

@spec slice(Range.t()) :: access_fun(data :: list(), current_value :: list())

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

Диапазон будет нормализован по тем же правилам, что и в Enum.slice/2.

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

Примеры

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> get_in(list, [Access.slice(1..2), :name])
["francine", "vitor"]
iex> get_and_update_in(list, [Access.slice(1..3//2), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{["francine"], [%{name: "john", salary: 10}, %{name: "FRANCINE", salary: 30}, %{name: "vitor", salary: 25}]}

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

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> pop_in(list, [Access.slice(-2..-1)])
{[%{name: "francine", salary: 30}, %{name: "vitor", salary: 25}], [%{name: "john", salary: 10}]}
iex> pop_in(list, [Access.slice(-2..-1), :name])
{["francine", "vitor"], [%{name: "john", salary: 10}, %{salary: 30}, %{salary: 25}]}

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

iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]
iex> get_in(list, [Access.slice(5..10//2), :name])
[]
iex> get_and_update_in(list, [Access.slice(5..10//2), :name], fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{[], [%{name: "john", salary: 10}, %{name: "francine", salary: 30}, %{name: "vitor", salary: 25}]}

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

iex> get_in(%{}, [Access.slice(2..10//3)])
** (ArgumentError) Access.slice/1 expected a list, got: %{}

Ошибка возникает, если шаг диапазона отрицательный:

iex> get_in([], [Access.slice(2..10//-1)])
** (ArgumentError) Access.slice/1 does not accept ranges with negative steps, got: 2..10//-1

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

Spec-Zone.ru

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