非常教程

Elixir 1.5参考手册

方法 | Access

方法 | Access

使用data[key]语法对数据结构进行基于密钥的访问。

Elixir为访问值提供了两种语法。user[:name]由动态结构使用,如地图和关键字,而user.name结构则使用它们。主要区别在于,user[:name]如果钥匙:name丢失将不会升起,但user.name如果没有:name钥匙则会升起钥匙。

除了上面的情况,这个模块提供了访问其他结构的便利函数,比如at/1列表和elem/1元组。这些功能可以通过在嵌套的更新功能一起使用Kernel,例如Kernel.get_in/2Kernel.put_in/3Kernel.update_in/3Kernel.get_and_update_in/3和朋友。

动态查找

开箱即用,AccessKeywordMap

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

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

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

请注意,动态查找语法(term[key])粗略地转换为Access.get(term, key, nil)

Access可以结合在一起Kernel.put_in/3给一个给定的键值:

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

这个语法非常方便,因为它可以任意嵌套:

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

此外,Access透明地忽略nil价值:

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

既然Access是一种行为,它可以用于键值数据结构。应该将实现添加到定义要访问的结构的模块中。Access要求使用===操作员来执行关键比较。

静态查找

Access语法(data[key])不能用于访问结构领域,因为结构不执行Access默认行为。这也是一个设计决策:动态访问查找用于动态键值结构,如地图和关键字,而不是像结构(其中字段已知而不是动态)那样的静态访问。

因此Elixir为结构字段和地图中的原子字段提供静态查找。想象一个User:name字段命名的结构。以下内容将引发:

user = %User{name: "John"}
user[:name]
# ** (UndefinedFunctionError) undefined function User.fetch/2 (User does not implement the Access behaviour)

结构改为使用user.name语法来访问字段:

user.name
#=> "John"

更新结构字段user.name也可以使用相同的语法Kernel.put_in/2

put_in user.name, "Mary"
#=> %User{name: "Mary"}

不同于user[:name]user.name不能通过行为扩展,仅限于地图中的结构和原子键。

如上所述,这也适用于地图中的原子键。Map有关这方面的更多信息,请参阅模块。

总结:

  • user[:name] 由动态结构使用,是可扩展的,不会丢失键
  • user.name 由静态结构使用,它不可扩展,它会在丢失的键上引发

访问器

尽管Elixir仅提供了用于遍历动态和静态键值结构的内置语法,但该模块提供了用于遍历其他结构(如元组和列表)的便利函数,以便与其他结构一起使用Kernel.put_in/2

例如,给定带有: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/1key!/1elem/1,和all/0一些可用的访问器。

实现自定义数据结构的访问行为

为了能够使用Access具有自定义数据结构(必须是结构)的Access行为,这些结构必须实现该行为。例如,对于一个User结构,这将不得不完成:

defmodule User do
  defstruct [:name, :email]

  @behaviour Access
  # Implementation of the Access callbacks...
end

类型

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行为的映射,关键字列表或结构)

get(container, key, default \ nil)

获取容器中给定键的值(实现该Access行为的映射,关键字列表或结构)

get_and_update(container, key, fun)

获取并更新给定的键container(映射,关键字列表,实现Access行为的结构)

key(key, default \ nil)

返回一个访问map/struct中给定键的函数

key!(key)

返回一个函数,函数在map/struct中访问给定的键。

pop(container, key)

从容器中移除具有给定键的条目(实现该Access行为的映射,关键字列表或结构)

回调

fetch(term, key)

调用以访问存储key在给定术语下的值term

get(term, key, default)

调用以访问key在给定期限内存储的值term,默认为default不存在

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/2Kernel.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(non_neg_integer) :: access_fun(data :: list, get_value :: term)

返回一个访问index列表中(基于零)元素的函数。

返回的函数是作为访问典型地过去了Kernel.get_in/2Kernel.get_and_update_in/3和朋友。

实例

iex> list = [%{name: "john"}, %{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"}]}

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)])
** (FunctionClauseError) no function clause matching in Access.at/1

如果访问的结构不是列表,则会引发错误:

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)

返回一个访问元组中给定索引元素的函数。

返回的函数是作为访问典型地过去了Kernel.get_in/2Kernel.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(nil_container, any) :: :error
fetch(container, term) :: {:ok, term} | :error

获取容器中的给定键的值(实现该Access行为的映射,关键字列表或结构)。

返回{:ok, value}这里value是下的值key,如果有这样的键,或者:error如果key没有找到。

get(container, key, default \ nil)

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

获取容器中给定键的值(实现该Access行为的映射,关键字列表或结构体)。

key如果有这样一个键,或者没有找到,default则返回下面的值key

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

获取并更新给定的键container(一个映射,一个关键字列表,一个实现Access行为的结构)。

fun参数接收key(或nil如果key不存在于container)值,并且必须返回一个双元素元组{get_value, update_value}:“get”值get_value(检索值,可以在返回之前对其进行操作)以及要存储的新值keyupdate_value)。fun也可能会返回:pop,这意味着当前值应该从容器中移除并返回。

返回的值是一个带有“get”值返回的两元素元组,fun以及一个具有更新值的新容器key

key(key, default \ nil)

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

返回一个访问map/struct中给定键的函数。

返回的函数是作为访问典型地过去了Kernel.get_in/2Kernel.get_and_update_in/3和朋友。

如果密钥不存在,则返回的函数使用默认值。这可以用来指定默认值并安全地遍历缺失的键:

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

这在使用更新函数时也很有用,允许我们在遍历数据结构进行更新时引入值:

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)

返回一个访问map/struct中给定键的函数。

返回的功能是作为访问典型地过去了Kernel.get_in/2Kernel.get_and_update_in/3和朋友。

如果密钥不存在,则返回的函数会上升。

实例

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

如果键中存在键,或者key:error该键在键词中不存在,则此函数应返回{:ok, value}value一个值。

Access模块中定义的许多函数在内部调用此函数。当使用方括号访问语法(structure[key])时,该函数也被使用:fetch/2定义structure结构的模块实现的回调被调用,并且如果返回{:ok, value}value返回,或者如果返回:errornil返回。

有关如何实现此回调的示例,请参阅Map.fetch/2Keyword.fetch/2实现。

get(term, key, default)

get(term :: t, key, default :: value) :: value

调用以访问key在给定期限内存储的值term,默认为default不存在。

这个函数应该在返回值keyterm,如果有这样的键,否则default

对于大多数数据结构,这可以在fetch/2内部实现; 例如:

def get(structure, key, default) do
  case fetch(structure, key) do
    {:ok, value} -> value
    :error       -> default
  end
end

有关如何实现此回调的示例,请参阅Map.get/3Keyword.get/3实现。

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或者nilif key不存在于其中。这个函数必须返回{get_value, update_value}或者:pop

如果传递函数返回{get_value, update_value},则此回调的返回值应为{get_value, new_data},其中:

  • get_value 是检索的值(可以在返回之前操作)
  • update_value 是要存储的新值 key
  • new_datadata在更新keywith 的值之后update_value

如果通过函数返回:pop,这个回调的返回值必须是{value, new_data}在那里value是下的值key(或者nil,如果不存在),并new_datadatakey

查看Map.get_and_update/3Keyword.get_and_update/3更多示例的实现。

pop(data, key)

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

被调用以将数值“弹出” key给定的数据结构之外。

key在给定结构存在data,执行应该返回一个{value, new_data}元组,其中value是下得值keynew_datatermkey

key不存在于给定结构中时,{value, data}应该返回一个元组,其中value是实现定义的。

查看实现Map.pop/3Keyword.pop/3更多示例。

方法 | Access相关

Elixir 1.5

Elixir 基于 Erlang 虚拟机的函数式、面向并行,是一种较好的编程语言。它以 Erlang 为基础,支持分布式、高容错、实时应用程序的开发。

主页 https://elixir-lang.org/
源码 https://github.com/elixir-lang/elixir
版本 1.5
发布版本 1.5.2