檢視原始碼 存取 行為 (Elixir v1.16.2)

基於金鑰存取資料結構。

存取 模組定義一個行為,用於透過 data[key] 語法動態存取資料結構中任何類型的金鑰。

存取 支援關鍵字清單 (關鍵字) 和對應 (對應)。關鍵字僅支援原子金鑰,對應的金鑰可以是任何類型。如果金鑰不存在,兩者都會傳回 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/2Kernel.update_in/2Kernel.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] 存取語法。 存取.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/2Kernel.put_in/3Kernel.update_in/3Kernel.pop_in/2Kernel.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}}

此模組提供便利函數來遍歷其他結構,例如元組和清單。這些函數可用於 Kernel 中所有與 Access 相關的函數和巨集。

例如,給定一個具有 :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/1all/0 以取得一些可用的存取器。

摘要

回呼

呼叫以存取儲存在給定術語 termkey 下的值。

呼叫以存取 key 下的值並同時更新它。

呼叫以從給定的資料結構中「彈出」key 下的值。

函數

傳回一個函數,用來存取清單中的所有元素。

傳回一個函數,用來存取清單中索引為 index (從 0 開始) 的元素。

at/1 相同,但如果指定的索引超出範圍,會引發 Enum.OutOfBoundsError

傳回一個函數,用來存取元組中指定索引的元素。

從容器中取得指定鍵的值(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

fetch/2 相同,但會直接傳回值,或者如果找不到 key,會引發 KeyError 例外。

傳回一個函數,用來存取清單中符合指定謂詞的所有元素。

從容器中取得指定鍵的值(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

取得並更新 container 中的指定鍵(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

傳回一個函數,用來存取映射/結構中的指定鍵。

傳回一個函數,用來存取映射/結構中的指定鍵。

從容器中移除具有指定鍵的項目(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

傳回一個函數,用來存取清單中位於指定範圍內的所有項目。

類型

連結到此類型

access_fun(data, current_value)

檢視原始碼
@type access_fun(data, current_value) ::
  get_fun(data) | get_and_update_fun(data, current_value)
@type container() :: keyword() | struct() | map()
連結到此類型

get_and_update_fun(data, current_value)

檢視原始碼
@type get_and_update_fun(data, current_value) ::
  (:get_and_update, data, (term() -> term()) ->
     {current_value, new_data :: container()} | :pop)
@type get_fun(data) :: (:get, data, (term() -> term()) -> new_data :: container())
@type key() :: any()
@type nil_container() :: nil
@type t() :: container() | nil_container() | any()
@type value() :: any()

呼叫回函式

@callback fetch(term :: t(), key()) :: {:ok, value()} | :error

呼叫以存取儲存在給定術語 termkey 下的值。

此函式應傳回 {:ok, value},其中 valuekey 底下的值(如果詞項中存在該鍵),或 :error(如果詞項中不存在該鍵)。

Access 模組中定義的許多函式在內部都會呼叫此函式。當使用方括號存取語法(structure[key])時也會使用此函式:由定義 structure 結構的模組實作的 fetch/2 呼叫回函式會被呼叫,如果它傳回 {:ok, value},則傳回 value,如果它傳回 :error,則傳回 nil

請參閱 Map.fetch/2Keyword.fetch/2 實作,以取得如何實作此回呼的範例。

連結到此呼叫回函式

get_and_update(data, key, function)

檢視原始碼
@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,並傳入傳遞結構 datakey 下的值,或傳入 nil(如果 key 不存在於其中)。此函式必須傳回 {current_value, new_value}:pop

如果傳遞的函式傳回 {current_value, new_value},此回呼的傳回值應為 {current_value, new_data},其中:

  • current_value 是已擷取的值(可以在傳回前進行運算)

  • new_value 是要儲存在 key 下的新值

  • new_data 是在使用 new_value 更新 key 的值後,所得到的 data

如果傳遞的函式傳回 :pop,此回呼的傳回值必須為 {value, new_data},其中 valuekey 下的值(如果不存在,則為 nil),而 new_data 是不包含 keydata

請參閱 Map.get_and_update/3Keyword.get_and_update/3 的實作,以取得更多範例。

@callback pop(data, key()) :: {value(), data} when data: container()

呼叫以從給定的資料結構中「彈出」key 下的值。

key 存在於指定的結構 data 中時,實作應傳回 {value, new_data} 組合,其中 valuekey 下的值,而 new_data 是不包含 keyterm

key 不存在於指定的結構中時,應傳回 {value, data} 組合,其中 value 由實作定義。

請參閱 Map.pop/3Keyword.pop/3 的實作,以取得更多範例。

函式

@spec all() :: access_fun(data :: list(), current_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: %{}
@spec at(integer()) :: access_fun(data :: list(), current_value :: term())

傳回一個函數,用來存取清單中索引為 index (從 0 開始) 的元素。

請記住,清單中的索引查詢需要線性時間:清單越大,存取其索引所需的時間就越長。因此,通常會避免使用基於索引的操作,而改用 Enum 模組中的其他函式。

傳回的函式通常會傳遞為存取器給 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_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)

檢視原始碼 (自 1.11.0 起)
@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
@spec elem(non_neg_integer()) :: access_fun(data :: tuple(), current_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: %{}
@spec fetch(container(), term()) :: {:ok, term()} | :error
@spec fetch(nil_container(), any()) :: :error

從容器中取得指定鍵的值(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

傳回 {:ok, value},其中 valuekey 下的值(如果有此鍵),或 :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)

檢視原始碼 (自 1.10.0 起)
@spec fetch!(container(), term()) :: term()

fetch/2 相同,但會直接傳回值,或者如果找不到 key,會引發 KeyError 例外。

範例

iex> Access.fetch!(%{name: "meg", age: 26}, :name)
"meg"
連結到此函式

filter(func)

檢視原始碼 (自 1.6.0 起)
@spec filter((term() -> boolean())) ::
  access_fun(data :: list(), current_value :: list())

傳回一個函數,用來存取清單中符合指定謂詞的所有元素。

傳回的函式通常會傳遞為存取器給 Kernel.get_in/2Kernel.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)

檢視原始碼
@spec get(container(), term(), term()) :: term()
@spec get(nil_container(), any(), default) :: default when default: var

從容器中取得指定鍵的值(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

如果存在 key,則傳回 key 下的值,如果找不到 key,則傳回 default

範例

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)

檢視原始碼
@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 的值(如果 container 中不存在 key,則為 nil),並且必須傳回一個二元組 {current_value, new_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]}
@spec key(key(), term()) ::
  access_fun(data :: struct() | map(), current_value :: term())

傳回一個函數,用來存取映射/結構中的指定鍵。

傳回的函式通常會傳遞為存取器給 Kernel.get_in/2Kernel.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: %{}}}

如果存取的結構不是 map 或 struct,會引發錯誤

iex> get_in([], [Access.key(:foo)])
** (BadMapError) expected a map, got: []
@spec key!(key()) :: access_fun(data :: struct() | map(), current_value :: term())

傳回一個函數,用來存取映射/結構中的指定鍵。

傳回的函式通常會傳遞為存取器給 Kernel.get_in/2Kernel.get_and_update_in/3 等函式。

類似於 key/2,但如果 key 不存在,回傳的函式會引發錯誤。

範例

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> map = %{user: %{name: "john"}}
iex> map.user.name
"john"
iex> get_and_update_in(map.user.name, fn prev ->
...>   {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}

不過,無法使用點號表示法移除欄位,因為它暗示這些欄位也必須存在。無論如何,Access.key!/1 在 key 未事先得知且必須動態存取時很有用。

如果存取的結構不是 map/struct,會引發錯誤

iex> get_in([], [Access.key!(:foo)])
** (RuntimeError) Access.key!/1 expected a map/struct, got: []
@spec pop(data, key()) :: {value(), data} when data: container()

從容器中移除具有指定鍵的項目(容器可以是映射、關鍵字清單或實作 Access 行為的結構)。

回傳一個包含與 key 關聯的值和更新後的容器的 tuple。如果 key 不在容器中,則會為值回傳 nil

範例

使用 map

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :name)
{"Elixir", %{creator: "Valim"}}

一個關鍵字清單

iex> Access.pop([name: "Elixir", creator: "Valim"], :name)
{"Elixir", [creator: "Valim"]}

一個未知的 key

iex> Access.pop(%{name: "Elixir", creator: "Valim"}, :year)
{nil, %{creator: "Valim", name: "Elixir"}}
連結到此函式

slice(range)

檢視原始碼 (自 1.14 起)
@spec slice(Range.t()) :: access_fun(data :: list(), current_value :: list())

傳回一個函數,用來存取清單中位於指定範圍內的所有項目。

範圍會根據 Enum.slice/2 的相同規則正規化。

傳回的函式通常會傳遞為存取器給 Kernel.get_in/2Kernel.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 也可以用來彈出清單中的元素或清單中的 key

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