Spec-Zone.ru › Elixir 1.15

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

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

Модуль 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]. Access.key/1 также можно использовать для построения динамического доступа к структурам и отображениям.

Вкратце, при использовании put_in/2 и его аналогов:

put_in(struct_or_map.key, :value)
put_in(keyword_or_map[:key], :value)

При использовании put_in/3 и его аналогов:

put_in(struct_or_map, [Access.key!(:key)], :value)
put_in(keyword_or_map, [:key], :value)

Это охватывает двойственную природу отображений в Elixir, так как они могут быть как для структурированных данных, так и для хранения пар ключ-значение. Подробнее см. модуль 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)

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

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)

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

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

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

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

Обратите внимание, что поиск по индексу в списках требует линейного времени: чем больше список, тем дольше будет выполняться доступ к его индексу. Поэтому операции по индексу обычно избегаются в пользу других функций в модуле Enum.

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

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()) :: {: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!(container, key)Source

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

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

Примеры

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

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" value) current_value (полученное значение, которое можно обработать перед возвратом) и новое значение, которое должно быть сохранено по ключу key (new_value). fun также может вернуть :pop, что означает, что текущее значение должно быть удалено из контейнера и возвращено.

Возвращаемое значение — кортеж из двух элементов: значение, возвращённое функцией 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)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!(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: []
END_OF_DOCUMENT_MARKER

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

slice(range)Source

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

Spec-Zone.ru

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