方法 | Access
存取行为
使用data[key]
语法对数据结构进行基于密钥的访问。
Elixir为访问值提供了两种语法。user[:name]
由动态结构使用,如地图和关键字,而user.name
结构则使用它们。主要区别在于,user[:name]
如果钥匙:name
丢失将不会升起,但user.name
如果没有:name
钥匙则会升起钥匙。
除了上面的情况,这个模块提供了访问其他结构的便利函数,比如at/1
列表和elem/1
元组。这些功能可以通过在嵌套的更新功能一起使用Kernel
,例如Kernel.get_in/2
,Kernel.put_in/3
,Kernel.update_in/3
,Kernel.get_and_update_in/3
和朋友。
动态查找
开箱即用,Access
并Keyword
与Map
:
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/1
,key!/1
,elem/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/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)
at(non_neg_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_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/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)
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
(检索值,可以在返回之前对其进行操作)以及要存储的新值key
(update_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/2
,Kernel.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/2
,Kernel.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
返回,或者如果返回:error
则nil
返回。
有关如何实现此回调的示例,请参阅Map.fetch/2
和Keyword.fetch/2
实现。
get(term, key, default)
get(term :: t, key, default :: value) :: value
调用以访问key
在给定期限内存储的值term
,默认为default
不存在。
这个函数应该在返回值key
中term
,如果有这样的键,否则default
。
对于大多数数据结构,这可以在fetch/2
内部实现; 例如:
def get(structure, key, default) do
case fetch(structure, key) do
{:ok, value} -> value
:error -> default
end
end
有关如何实现此回调的示例,请参阅Map.get/3
和Keyword.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
或者nil
if key
不存在于其中。这个函数必须返回{get_value, update_value}
或者:pop
。
如果传递函数返回{get_value, update_value}
,则此回调的返回值应为{get_value, new_data}
,其中:
get_value
是检索的值(可以在返回之前操作)
update_value
是要存储的新值key
new_data
是data
在更新key
with 的值之后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
更多示例。