Spec-Zone.ru › Elixir 1.9

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

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

Модуль 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).

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

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

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

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

at(integer()) :: access_fun(data :: list(), get_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(индекс)

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

elem(non_neg_integer()) :: access_fun(data :: tuple(), get_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(контейнер, ключ)

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

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

filter(функция)

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

filter((term() -> boolean())) :: access_fun(data :: list(), get_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)

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

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(контейнер, ключ, функция)

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

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

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

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

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

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

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

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

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

Возвращаемая функция обычно передаётся в качестве аксессора к 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()) :: access_fun(data :: struct() | map(), get_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(контейнер, ключ)

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

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.9.4/Access.html

Spec-Zone.ru

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