查看源代码 Enum (Elixir v1.16.2)

用于处理集合(称为可枚举对象)的函数。

在 Elixir 中,可枚举对象是指任何实现了 Enumerable 协议的数据类型。 Lists ([1, 2, 3])、Maps (%{foo: 1, bar: 2}) 和 Ranges (1..3) 是常用作可枚举对象的数据类型。

iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]

iex> Enum.sum([1, 2, 3])
6

iex> Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]

iex> Enum.sum(1..3)
6

iex> map = %{"a" => 1, "b" => 2}
iex> Enum.map(map, fn {k, v} -> {k, v * 2} end)
[{"a", 2}, {"b", 4}]

语言中存在许多其他可枚举对象,例如 MapSets 和 File.stream!/3 返回的数据类型,它允许像枚举对象一样遍历文件。

有关 Enum 模块中所有函数的概述,请参阅 Enum 速查表

此模块中的函数以线性时间工作。这意味着执行操作所需的时间与可枚举对象的长度增长速度相同。这在 Enum.map/2 等操作中是预期的。毕竟,如果我们要遍历列表中的每个元素,列表越长,我们需要遍历的元素就越多,花费的时间就越长。

这种线性行为也应该在 count/1member?/2at/2 等操作中预料到。虽然 Elixir 允许数据类型为这些操作提供高性能变体,但你不应该期望它总是可用,因为 Enum 模块旨在与各种数据类型一起工作,并非所有数据类型都能提供优化行为。

最后,请注意 Enum 模块中的函数是急切的:它们将在被调用时立即遍历可枚举对象。这在处理无限可枚举对象时特别危险。在这种情况下,你应该使用 Stream 模块,它允许你以惰性方式表达计算,而无需遍历集合,并处理可能无限的集合。有关示例和文档,请参阅 Stream 模块。

摘要

类型

从零开始的索引。它也可以是负整数。

t()

函数

如果 enumerable 中的所有元素都为真值,则返回 true

如果 enumerable 中所有元素的 fun.(element) 都为真值,则返回 true

如果 enumerable 中至少有一个元素为真值,则返回 true

如果 enumerable 中至少有一个元素的 fun.(element) 为真值,则返回 true

查找给定 index(从零开始)处的元素。

fun 返回新值的每个元素上拆分可枚举对象。

chunk_every(enumerable, count, count) 的快捷方式。

返回包含 count 个元素的列表,每个列表包含 count 个元素,其中每个新块都从 enumerable 中的 step 个元素开始。

使用精细的控制对 enumerable 进行分块,当发出每个块时。

给定可枚举对象的枚举对象,将 enumerables 连接成一个单一的列表。

right 上的可枚举对象与 left 上的可枚举对象连接起来。

返回 enumerable 的大小。

返回 enumerablefun 返回真值的元素个数。

计算可枚举对象,在 limit 处停止。

计算可枚举对象中 fun 返回真值的元素个数,在 limit 处停止。

枚举 enumerable,返回一个列表,其中所有连续的重复元素都折叠为一个元素。

枚举 enumerable,返回一个列表,其中所有连续的重复元素都折叠为一个元素。

enumerable 中删除 amount 个元素。

返回一个列表,该列表包含从第一个元素开始,从 enumerable 中删除的每个 nth 个元素。

删除 enumerable 开始处的元素,直到 fun 返回真值。

enumerable 中的每个元素调用给定的 fun

确定 enumerable 是否为空。

查找给定 index(从零开始)处的元素。

查找给定 index(从零开始)处的元素。

过滤 enumerable,即仅返回 fun 返回真值的那些元素。

返回 fun 返回真值的第一个元素。如果找不到这样的元素,则返回 default

类似于 find/3,但返回元素的索引(从零开始),而不是元素本身。

类似于 find/3,但返回函数调用的值,而不是元素本身。

enumerable 上的给定 fun 进行映射,并展平结果。

映射和减少 enumerable,展平给定的结果(仅在一级深度)。

返回一个映射,其键是 enumerable 的唯一元素,其值为每个元素的计数。

返回一个映射,其键是 key_fun 给出的唯一元素,其值为每个元素的计数。

根据 key_funenumerable 分成组。

在枚举的每个元素之间插入 separator

将给定的 enumerable 插入到 collectable 中。

根据转换函数将给定的 enumerable 插入到 collectable 中。

使用 joiner 作为分隔符,将给定的 enumerable 连接成一个字符串。

返回一个列表,其中每个元素都是对 enumerable 的每个对应元素调用 fun 的结果。

返回一个列表,该列表包含对 enumerable 的每个 nth 个元素调用 fun 的结果,从第一个元素开始。

在一遍中映射和插入给定的可枚举对象。

在一遍中映射和连接给定的 enumerable

enumerable 中的每个元素调用给定的函数,将其简化为单个元素,同时保持累加器。

根据 Erlang 的项排序返回 enumerable 中的最大元素。

返回 enumerable 中由给定 fun 计算的最大元素。

检查 element 是否存在于 enumerable 中。

根据 Erlang 的项排序返回 enumerable 中的最小元素。

返回 enumerable 中由给定 fun 计算的最小元素。

返回一个元组,其中包含可枚举对象中根据 Erlang 的项排序的最小和最大元素。

返回一个元组,其中包含可枚举对象中由给定函数计算的最小和最大元素。

返回所有元素的乘积。

返回 enumerable 的随机元素。

enumerable 中的每个元素调用 fun,并使用累加器。

enumerable 中的每个元素调用 fun,并使用累加器。

减少 enumerable,直到 fun 返回 {:halt, term}

返回 enumerable 中的元素列表,不包括函数 fun 返回真值的那些元素。

返回一个列表,其中包含 enumerable 中的元素,按相反顺序排列。

反转 enumerable 中的元素,追加 tail,并将其作为列表返回。

反转 enumerable 中从初始 start_index 开始到 count 个元素的范围内的元素。

将给定的函数应用于 enumerable 中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用 enumerable 中的第一个元素作为起始值。

将给定的函数应用于 enumerable 中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用给定的 acc 作为起始值。

返回一个列表,其中包含 enumerable 的元素,已洗牌。

通过 index_range 返回给定 enumerable 的子集列表。

返回给定 enumerable 的子集列表,从 start_index(从零开始)开始,包含 amount 个元素(如果有)。

range_or_single_index 给出的单个或多个元素从 enumerable 滑动到 insertion_index

根据 Erlang 的项排序对 enumerable 进行排序。

根据给定的函数对 enumerable 进行排序。

根据提供的 sorter 函数对 enumerable 的映射结果进行排序。

enumerable 分割成两个可枚举对象,第一个可枚举对象包含 count 个元素。

在第一次 fun 对元素返回假值 (falsenil) 的位置将可枚举对象分割成两个。

根据给定的函数 funenumerable 分割成两个列表。

返回所有元素的总和。

enumerable 的开头或结尾获取 amount 个元素。

返回 enumerable 中每隔 nth 个元素的列表,从第一个元素开始。

enumerable 中获取 count 个随机元素。

enumerable 的开头获取元素,直到 fun 返回真值。

enumerable 转换为列表。

枚举 enumerable,删除所有重复元素。

枚举 enumerable,通过删除函数 fun 返回重复元素的元素来完成。

zip/2 相反。从给定的 enumerable 中提取二元组,并将它们组合在一起。

返回 enumerable,其中每个元素都与其索引一起包装在元组中。

将来自有限的可枚举对象集合的对应元素压缩成元组列表。

将来自两个可枚举对象的对应元素压缩成元组列表。

对所有给定的可枚举对象进行归约,只要任何可枚举对象为空就停止。

对两个可枚举对象进行归约,只要任何一个可枚举对象为空就停止。

将来自有限的可枚举对象集合的对应元素压缩成列表,并在压缩过程中使用 zip_fun 函数对它们进行转换。

将来自两个可枚举对象的对应元素压缩成列表,并在压缩过程中使用 zip_fun 函数对它们进行转换。

类型

@type acc() :: any()
@type default() :: any()
@type element() :: any()
@type index() :: integer()

从零开始的索引。它也可以是负整数。

@type t() :: Enumerable.t()

函数

@spec all?(t()) :: boolean()

如果 enumerable 中的所有元素都为真值,则返回 true

当元素的值为假值 (falsenil) 时,迭代立即停止,并返回 false。在所有其他情况下,返回 true

示例

iex> Enum.all?([1, 2, 3])
true

iex> Enum.all?([1, nil, 3])
false

iex> Enum.all?([])
true
@spec all?(t(), (element() -> as_boolean(term()))) :: boolean()

如果 enumerable 中所有元素的 fun.(element) 都为真值,则返回 true

迭代 enumerable,并对每个元素调用 fun。如果 fun 曾经返回假值 (falsenil),迭代立即停止,并返回 false。否则,返回 true

示例

iex> Enum.all?([2, 4, 6], fn x -> rem(x, 2) == 0 end)
true

iex> Enum.all?([2, 3, 4], fn x -> rem(x, 2) == 0 end)
false

iex> Enum.all?([], fn _ -> nil end)
true

如最后一个示例所示,Enum.all?/2 如果 enumerable 为空,则返回 true,无论 fun 是什么。在一个空的可枚举对象中,没有元素可以使 fun 返回假值,因此结果必须是 true。这是一个对空集合的明确定义的逻辑论证。

@spec any?(t()) :: boolean()

如果 enumerable 中至少有一个元素为真值,则返回 true

当元素的值为真值(既不是 false 也不是 nil) 时,迭代立即停止,并返回 true。在所有其他情况下,返回 false

示例

iex> Enum.any?([false, false, false])
false

iex> Enum.any?([false, true, false])
true

iex> Enum.any?([])
false
@spec any?(t(), (element() -> as_boolean(term()))) :: boolean()

如果 enumerable 中至少有一个元素的 fun.(element) 为真值,则返回 true

迭代 enumerable,并对每个元素调用 fun。当 fun 的调用返回真值(既不是 false 也不是 nil) 时,迭代立即停止,并返回 true。在所有其他情况下,返回 false

示例

iex> Enum.any?([2, 4, 6], fn x -> rem(x, 2) == 1 end)
false

iex> Enum.any?([2, 3, 4], fn x -> rem(x, 2) == 1 end)
true

iex> Enum.any?([], fn x -> x > 0 end)
false
链接到此函数

at(enumerable, index, default \\ nil)

查看源代码
@spec at(t(), index(), default()) :: element() | default()

查找给定 index(从零开始)处的元素。

如果 index 超出范围,则返回 default

可以传递负的 index,这意味着 enumerable 被枚举一次,并且 index 从末尾开始计数(例如,-1 找到最后一个元素)。

示例

iex> Enum.at([2, 4, 6], 0)
2

iex> Enum.at([2, 4, 6], 2)
6

iex> Enum.at([2, 4, 6], 4)
nil

iex> Enum.at([2, 4, 6], 4, :none)
:none
链接到此函数

chunk_by(enumerable, fun)

查看源代码
@spec chunk_by(t(), (element() -> any())) :: [list()]

fun 返回新值的每个元素上拆分可枚举对象。

返回一个列表的列表。

示例

iex> Enum.chunk_by([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1))
[[1], [2, 2], [3], [4, 4, 6], [7, 7]]
链接到此函数

chunk_every(enumerable, count)

查看源代码 (自 1.5.0 起)
@spec chunk_every(t(), pos_integer()) :: [list()]

chunk_every(enumerable, count, count) 的快捷方式。

链接到此函数

chunk_every(enumerable, count, step, leftover \\ [])

查看源代码 (自 1.5.0 起)
@spec chunk_every(t(), pos_integer(), pos_integer(), t() | :discard) :: [list()]

返回包含 count 个元素的列表,每个列表包含 count 个元素,其中每个新块都从 enumerable 中的 step 个元素开始。

step 是可选的,如果未传入,则默认为 count,即块不重叠。当集合结束或我们发出一个不完整的块时,块操作将停止。

如果最后一个块没有足够的元素来填满块,则会从 leftover 中获取元素来填满块。如果 leftover 没有足够的元素来填满块,则返回一个部分块,其中元素少于 count

如果 leftover 中提供了 :discard,则最后一个块将被丢弃,除非它正好包含 count 个元素。

示例

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 2, :discard)
[[1, 2, 3], [3, 4, 5]]

iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 2, [7])
[[1, 2, 3], [3, 4, 5], [5, 6, 7]]

iex> Enum.chunk_every([1, 2, 3, 4], 3, 3, [])
[[1, 2, 3], [4]]

iex> Enum.chunk_every([1, 2, 3, 4], 10)
[[1, 2, 3, 4]]

iex> Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, [])
[[1, 2], [4, 5]]

iex> Enum.chunk_every([1, 2, 3, 4], 3, 3, Stream.cycle([0]))
[[1, 2, 3], [4, 0, 0]]
链接到此函数

chunk_while(enumerable, acc, chunk_fun, after_fun)

查看源代码 (自 1.5.0 起)
@spec chunk_while(
  t(),
  acc(),
  (element(), acc() -> {:cont, chunk, acc()} | {:cont, acc()} | {:halt, acc()}),
  (acc() -> {:cont, chunk, acc()} | {:cont, acc()})
) :: Enumerable.t()
when chunk: any()

使用精细的控制对 enumerable 进行分块,当发出每个块时。

chunk_fun 接收当前元素和累加器,必须返回

  • {:cont, chunk, acc} 来发出一个块并继续使用累加器
  • {:cont, acc} 来不发出任何块并继续使用累加器
  • {:halt, acc} 来停止对 enumerable 的块操作。

after_fun 使用完成遍历(或 halted)后的最终累加器调用,以处理作为累加器一部分返回但未由 chunk_fun 作为块发出的任何尾随元素。它必须返回

  • {:cont, chunk, acc} 来发出一个块。该块将被追加到已发出的块列表中。
  • {:cont, acc} 来不发出一个块

after_fun 中的 acc 是必需的,以便镜像 chunk_fun 的元组格式,但它将被丢弃,因为遍历已完成。

返回一个已发出的块列表。

示例

iex> chunk_fun = fn element, acc ->
...>   if rem(element, 2) == 0 do
...>     {:cont, Enum.reverse([element | acc]), []}
...>   else
...>     {:cont, [element | acc]}
...>   end
...> end
iex> after_fun = fn
...>   [] -> {:cont, []}
...>   acc -> {:cont, Enum.reverse(acc), []}
...> end
iex> Enum.chunk_while(1..10, [], chunk_fun, after_fun)
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
iex> Enum.chunk_while([1, 2, 3, 5, 7], [], chunk_fun, after_fun)
[[1, 2], [3, 5, 7]]
@spec concat(t()) :: t()

给定可枚举对象的枚举对象,将 enumerables 连接成一个单一的列表。

示例

iex> Enum.concat([1..3, 4..6, 7..9])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

iex> Enum.concat([[1, [2], 3], [4], [5, 6]])
[1, [2], 3, 4, 5, 6]
@spec concat(t(), t()) :: t()

right 上的可枚举对象与 left 上的可枚举对象连接起来。

此函数产生的结果与列表的 ++/2 运算符相同。

示例

iex> Enum.concat(1..3, 4..6)
[1, 2, 3, 4, 5, 6]

iex> Enum.concat([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
@spec count(t()) :: non_neg_integer()

返回 enumerable 的大小。

示例

iex> Enum.count([1, 2, 3])
3
链接到此函数

count(enumerable, fun)

查看源代码
@spec count(t(), (element() -> as_boolean(term()))) :: non_neg_integer()

返回 enumerablefun 返回真值的元素个数。

示例

iex> Enum.count([1, 2, 3, 4, 5], fn x -> rem(x, 2) == 0 end)
2
链接到此函数

count_until(enumerable, limit)

查看源代码 (自 1.12.0 起)
@spec count_until(t(), pos_integer()) :: non_neg_integer()

计算可枚举对象,在 limit 处停止。

这对于在不实际计数整个可枚举对象的情况下检查可枚举对象的计数的某些属性很有用。例如,如果您想检查计数是否正好、至少或多于某个值。

如果可枚举对象实现了 Enumerable.count/1,则不会遍历可枚举对象,我们返回两个数字中较小的一个。要强制枚举,请使用 count_until/3,并将 fn _ -> true end 作为第二个参数。

示例

iex> Enum.count_until(1..20, 5)
5
iex> Enum.count_until(1..20, 50)
20
iex> Enum.count_until(1..10, 10) == 10 # At least 10
true
iex> Enum.count_until(1..11, 10 + 1) > 10 # More than 10
true
iex> Enum.count_until(1..5, 10) < 10 # Less than 10
true
iex> Enum.count_until(1..10, 10 + 1) == 10 # Exactly ten
true
链接到此函数

count_until(enumerable, fun, limit)

查看源代码 (自 1.12.0 起)
@spec count_until(t(), (element() -> as_boolean(term())), pos_integer()) ::
  non_neg_integer()

计算可枚举对象中 fun 返回真值的元素个数,在 limit 处停止。

有关更多信息,请参阅 count/2count_until/2

示例

iex> Enum.count_until(1..20, fn x -> rem(x, 2) == 0 end, 7)
7
iex> Enum.count_until(1..20, fn x -> rem(x, 2) == 0 end, 11)
10
@spec dedup(t()) :: list()

枚举 enumerable,返回一个列表,其中所有连续的重复元素都折叠为一个元素。

元素使用 ===/2 进行比较。

如果您想删除所有重复元素,无论顺序如何,请参阅 uniq/1

示例

iex> Enum.dedup([1, 2, 3, 3, 2, 1])
[1, 2, 3, 2, 1]

iex> Enum.dedup([1, 1, 2, 2.0, :three, :three])
[1, 2, 2.0, :three]
链接到此函数

dedup_by(enumerable, fun)

查看源代码
@spec dedup_by(t(), (element() -> term())) :: list()

枚举 enumerable,返回一个列表,其中所有连续的重复元素都折叠为一个元素。

函数 fun 将每个元素映射到一个项,该项用于确定两个元素是否为重复元素。

示例

iex> Enum.dedup_by([{1, :a}, {2, :b}, {2, :c}, {1, :a}], fn {x, _} -> x end)
[{1, :a}, {2, :b}, {1, :a}]

iex> Enum.dedup_by([5, 1, 2, 3, 2, 1], fn x -> x > 2 end)
[5, 1, 3, 2]
链接到此函数

drop(enumerable, amount)

查看源代码
@spec drop(t(), integer()) :: list()

enumerable 中删除 amount 个元素。

如果给出了负的 amount,则将删除 amount 个最后的元素。将枚举 enumerable 一次以检索正确的索引,其余计算将从末尾开始执行。

示例

iex> Enum.drop([1, 2, 3], 2)
[3]

iex> Enum.drop([1, 2, 3], 10)
[]

iex> Enum.drop([1, 2, 3], 0)
[1, 2, 3]

iex> Enum.drop([1, 2, 3], -1)
[1, 2]
链接到此函数

drop_every(enumerable, nth)

查看源代码
@spec drop_every(t(), non_neg_integer()) :: list()

返回一个列表,该列表包含从第一个元素开始,从 enumerable 中删除的每个 nth 个元素。

第一个元素总是被删除,除非 nth 为 0。

指定每隔 nth 个元素的第二个参数必须是非负整数。

示例

iex> Enum.drop_every(1..10, 2)
[2, 4, 6, 8, 10]

iex> Enum.drop_every(1..10, 0)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

iex> Enum.drop_every([1, 2, 3], 1)
[]
链接到此函数

drop_while(enumerable, fun)

查看源代码
@spec drop_while(t(), (element() -> as_boolean(term()))) :: list()

删除 enumerable 开始处的元素,直到 fun 返回真值。

示例

iex> Enum.drop_while([1, 2, 3, 2, 1], fn x -> x < 3 end)
[3, 2, 1]
@spec each(t(), (element() -> any())) :: :ok

enumerable 中的每个元素调用给定的 fun

返回 :ok

示例

Enum.each(["some", "example"], fn x -> IO.puts(x) end)
"some"
"example"
#=> :ok
@spec empty?(t()) :: boolean()

确定 enumerable 是否为空。

如果 enumerable 为空,则返回 true,否则返回 false

示例

iex> Enum.empty?([])
true

iex> Enum.empty?([1, 2, 3])
false
链接到此函数

fetch(enumerable, index)

查看源代码
@spec fetch(t(), index()) :: {:ok, element()} | :error

查找给定 index(从零开始)处的元素。

如果找到,则返回 {:ok, element},否则返回 :error

可以传递负数 index,这意味着 enumerable 会被枚举一次,并且 index 从末尾开始计数(例如,-1 获取最后一个元素)。

示例

iex> Enum.fetch([2, 4, 6], 0)
{:ok, 2}

iex> Enum.fetch([2, 4, 6], -3)
{:ok, 2}

iex> Enum.fetch([2, 4, 6], 2)
{:ok, 6}

iex> Enum.fetch([2, 4, 6], 4)
:error
链接到此函数

fetch!(enumerable, index)

查看源代码
@spec fetch!(t(), index()) :: element()

查找给定 index(从零开始)处的元素。

如果给定的 index 超出 enumerable 的范围,则引发 OutOfBoundsError

示例

iex> Enum.fetch!([2, 4, 6], 0)
2

iex> Enum.fetch!([2, 4, 6], 2)
6

iex> Enum.fetch!([2, 4, 6], 4)
** (Enum.OutOfBoundsError) out of bounds error
链接到此函数

filter(enumerable, fun)

查看源代码
@spec filter(t(), (element() -> as_boolean(term()))) :: list()

过滤 enumerable,即仅返回 fun 返回真值的那些元素。

另请参见 reject/2,它会丢弃函数返回真值的所有元素。

示例

iex> Enum.filter([1, 2, 3], fn x -> rem(x, 2) == 0 end)
[2]

请记住,filter 无法同时过滤和转换元素。如果您想这样做,请考虑使用 flat_map/2。例如,如果您想将所有表示整数的字符串转换为整数,并在一步中丢弃无效的字符串

strings = ["1234", "abc", "12ab"]

Enum.flat_map(strings, fn string ->
  case Integer.parse(string) do
    # transform to integer
    {int, _rest} -> [int]
    # skip the value
    :error -> []
  end
end)
链接到此函数

find(enumerable, default \\ nil, fun)

查看源代码
@spec find(t(), default(), (element() -> any())) :: element() | default()

返回 fun 返回真值的第一个元素。如果找不到这样的元素,则返回 default

示例

iex> Enum.find([2, 3, 4], fn x -> rem(x, 2) == 1 end)
3

iex> Enum.find([2, 4, 6], fn x -> rem(x, 2) == 1 end)
nil
iex> Enum.find([2, 4, 6], 0, fn x -> rem(x, 2) == 1 end)
0
链接到此函数

find_index(enumerable, fun)

查看源代码
@spec find_index(t(), (element() -> any())) :: non_neg_integer() | nil

类似于 find/3,但返回元素的索引(从零开始),而不是元素本身。

示例

iex> Enum.find_index([2, 4, 6], fn x -> rem(x, 2) == 1 end)
nil

iex> Enum.find_index([2, 3, 4], fn x -> rem(x, 2) == 1 end)
1
链接到此函数

find_value(enumerable, default \\ nil, fun)

查看源代码
@spec find_value(t(), default(), (element() -> found_value)) ::
  found_value | default()
when found_value: term()

类似于 find/3,但返回函数调用的值,而不是元素本身。

当结果为真值(既不是 nil 也不是 false)时,返回值被认为是找到的。

示例

iex> Enum.find_value([2, 3, 4], fn x ->
...>   if x > 2, do: x * x
...> end)
9

iex> Enum.find_value([2, 4, 6], fn x -> rem(x, 2) == 1 end)
nil

iex> Enum.find_value([2, 3, 4], fn x -> rem(x, 2) == 1 end)
true

iex> Enum.find_value([1, 2, 3], "no bools!", &is_boolean/1)
"no bools!"
链接到此函数

flat_map(enumerable, fun)

查看源代码
@spec flat_map(t(), (element() -> t())) :: list()

enumerable 上的给定 fun 进行映射,并展平结果。

此函数返回一个新的可枚举对象,该对象通过将对 enumerable 的每个元素调用 fun 的结果连接在一起而构建;从概念上讲,这类似于 map/2concat/1 的组合。

示例

iex> Enum.flat_map([:a, :b, :c], fn x -> [x, x] end)
[:a, :a, :b, :b, :c, :c]

iex> Enum.flat_map([{1, 3}, {4, 6}], fn {x, y} -> x..y end)
[1, 2, 3, 4, 5, 6]

iex> Enum.flat_map([:a, :b, :c], fn x -> [[x]] end)
[[:a], [:b], [:c]]
链接到此函数

flat_map_reduce(enumerable, acc, fun)

查看源代码
@spec flat_map_reduce(t(), acc(), fun) :: {[any()], acc()}
when fun: (element(), acc() -> {t(), acc()} | {:halt, acc()})

映射和减少 enumerable,展平给定的结果(仅在一级深度)。

它需要一个累加器和一个接收每个可枚举元素的函数,并且必须返回一个元组,其中包含一个新的可枚举对象(通常是一个列表)以及新的累加器,或者一个包含 :halt 作为第一个元素和累加器作为第二个元素的元组。

示例

iex> enumerable = 1..100
iex> n = 3
iex> Enum.flat_map_reduce(enumerable, 0, fn x, acc ->
...>   if acc < n, do: {[x], acc + 1}, else: {:halt, acc}
...> end)
{[1, 2, 3], 3}

iex> Enum.flat_map_reduce(1..5, 0, fn x, acc -> {[[x]], acc + x} end)
{[[1], [2], [3], [4], [5]], 15}
链接到此函数

frequencies(enumerable)

查看源代码 (自 1.10.0 起)
@spec frequencies(t()) :: map()

返回一个映射,其键是 enumerable 的唯一元素,其值为每个元素的计数。

示例

iex> Enum.frequencies(~w{ant buffalo ant ant buffalo dingo})
%{"ant" => 3, "buffalo" => 2, "dingo" => 1}
链接到此函数

frequencies_by(enumerable, key_fun)

查看源代码 (自 1.10.0 起)
@spec frequencies_by(t(), (element() -> any())) :: map()

返回一个映射,其键是 key_fun 给出的唯一元素,其值为每个元素的计数。

示例

iex> Enum.frequencies_by(~w{aa aA bb cc}, &String.downcase/1)
%{"aa" => 2, "bb" => 1, "cc" => 1}

iex> Enum.frequencies_by(~w{aaa aA bbb cc c}, &String.length/1)
%{3 => 2, 2 => 2, 1 => 1}
链接到此函数

group_by(enumerable, key_fun, value_fun \\ fn x -> x end)

查看源代码
@spec group_by(t(), (element() -> any()), (element() -> any())) :: map()

根据 key_funenumerable 分成组。

结果是一个映射,其中每个键由 key_fun 给出,每个值是 value_fun 给出的元素列表。每个列表中元素的顺序保留自 enumerable。但是,像所有映射一样,结果映射是无序的。

示例

iex> Enum.group_by(~w{ant buffalo cat dingo}, &String.length/1)
%{3 => ["ant", "cat"], 5 => ["dingo"], 7 => ["buffalo"]}

iex> Enum.group_by(~w{ant buffalo cat dingo}, &String.length/1, &String.first/1)
%{3 => ["a", "c"], 5 => ["d"], 7 => ["b"]}

键可以是任何 Elixir 值。例如,您可以使用元组按多个键进行分组

iex> collection = [
...>   %{id: 1, lang: "Elixir", seq: 1},
...>   %{id: 1, lang: "Java", seq: 1},
...>   %{id: 1, lang: "Ruby", seq: 2},
...>   %{id: 2, lang: "Python", seq: 1},
...>   %{id: 2, lang: "C#", seq: 2},
...>   %{id: 2, lang: "Haskell", seq: 2},
...> ]
iex> Enum.group_by(collection, &{&1.id, &1.seq})
%{
  {1, 1} => [%{id: 1, lang: "Elixir", seq: 1}, %{id: 1, lang: "Java", seq: 1}],
  {1, 2} => [%{id: 1, lang: "Ruby", seq: 2}],
  {2, 1} => [%{id: 2, lang: "Python", seq: 1}],
  {2, 2} => [%{id: 2, lang: "C#", seq: 2}, %{id: 2, lang: "Haskell", seq: 2}]
}
iex> Enum.group_by(collection, &{&1.id, &1.seq}, &{&1.id, &1.lang})
%{
  {1, 1} => [{1, "Elixir"}, {1, "Java"}],
  {1, 2} => [{1, "Ruby"}],
  {2, 1} => [{2, "Python"}],
  {2, 2} => [{2, "C#"}, {2, "Haskell"}]
}
链接到此函数

intersperse(enumerable, separator)

查看源代码
@spec intersperse(t(), element()) :: list()

在枚举的每个元素之间插入 separator

示例

iex> Enum.intersperse([1, 2, 3], 0)
[1, 0, 2, 0, 3]

iex> Enum.intersperse([1], 0)
[1]

iex> Enum.intersperse([], 0)
[]
链接到此函数

into(enumerable, collectable)

查看源代码
@spec into(Enumerable.t(), Collectable.t()) :: Collectable.t()

将给定的 enumerable 插入到 collectable 中。

请注意,传递非空列表作为 collectable 已被弃用。如果您正在收集到非空的关键字列表中,请考虑使用 Keyword.merge(collectable, Enum.to_list(enumerable))。如果您正在收集到非空的列表中,请考虑类似 Enum.to_list(enumerable) ++ collectable 的方法。

示例

iex> Enum.into([1, 2], [])
[1, 2]

iex> Enum.into([a: 1, b: 2], %{})
%{a: 1, b: 2}

iex> Enum.into(%{a: 1}, %{b: 2})
%{a: 1, b: 2}

iex> Enum.into([a: 1, a: 2], %{})
%{a: 2}

iex> Enum.into([a: 2], %{a: 1, b: 3})
%{a: 2, b: 3}
链接到此函数

into(enumerable, collectable, transform)

查看源代码
@spec into(Enumerable.t(), Collectable.t(), (term() -> term())) :: Collectable.t()

根据转换函数将给定的 enumerable 插入到 collectable 中。

示例

iex> Enum.into([1, 2, 3], [], fn x -> x * 3 end)
[3, 6, 9]

iex> Enum.into(%{a: 1, b: 2}, %{c: 3}, fn {k, v} -> {k, v * 2} end)
%{a: 2, b: 4, c: 3}
链接到此函数

join(enumerable, joiner \\ "")

查看源代码
@spec join(t(), String.t()) :: String.t()

使用 joiner 作为分隔符,将给定的 enumerable 连接成一个字符串。

如果完全不传递 joiner,则默认为空字符串。

enumerable 中的所有元素都必须可转换为字符串,否则会引发错误。

示例

iex> Enum.join([1, 2, 3])
"123"

iex> Enum.join([1, 2, 3], " = ")
"1 = 2 = 3"

iex> Enum.join([["a", "b"], ["c", "d", "e", ["f", "g"]], "h", "i"], " ")
"ab cdefg h i"
@spec map(t(), (element() -> any())) :: list()

返回一个列表,其中每个元素都是对 enumerable 的每个对应元素调用 fun 的结果。

对于映射,该函数需要键值元组。

示例

iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]

iex> Enum.map([a: 1, b: 2], fn {k, v} -> {k, -v} end)
[a: -1, b: -2]
链接到此函数

map_every(enumerable, nth, fun)

查看源代码 (自 1.4.0 起)
@spec map_every(t(), non_neg_integer(), (element() -> any())) :: list()

返回一个列表,该列表包含对 enumerable 的每个 nth 个元素调用 fun 的结果,从第一个元素开始。

第一个元素总是传递给给定的函数,除非 nth0

指定每隔 nth 个元素的第二个参数必须是非负整数。

如果 nth0,则 enumerable 会直接转换为列表,而不会应用 fun

示例

iex> Enum.map_every(1..10, 2, fn x -> x + 1000 end)
[1001, 2, 1003, 4, 1005, 6, 1007, 8, 1009, 10]

iex> Enum.map_every(1..10, 3, fn x -> x + 1000 end)
[1001, 2, 3, 1004, 5, 6, 1007, 8, 9, 1010]

iex> Enum.map_every(1..5, 0, fn x -> x + 1000 end)
[1, 2, 3, 4, 5]

iex> Enum.map_every([1, 2, 3], 1, fn x -> x + 1000 end)
[1001, 1002, 1003]
链接到此函数

map_intersperse(enumerable, separator, mapper)

查看源代码 (自 1.10.0 起)
@spec map_intersperse(t(), element(), (element() -> any())) :: list()

在一遍中映射和插入给定的可枚举对象。

示例

iex> Enum.map_intersperse([1, 2, 3], :a, &(&1 * 2))
[2, :a, 4, :a, 6]
链接到此函数

map_join(enumerable, joiner \\ "", mapper)

查看源代码
@spec map_join(t(), String.t(), (element() -> String.Chars.t())) :: String.t()

在一遍中映射和连接给定的 enumerable

如果完全不传递 joiner,则默认为空字符串。

调用 mapper 返回的所有元素都必须可转换为字符串,否则会引发错误。

示例

iex> Enum.map_join([1, 2, 3], &(&1 * 2))
"246"

iex> Enum.map_join([1, 2, 3], " = ", &(&1 * 2))
"2 = 4 = 6"
链接到此函数

map_reduce(enumerable, acc, fun)

查看源代码
@spec map_reduce(t(), acc(), (element(), acc() -> {element(), acc()})) ::
  {list(), acc()}

enumerable 中的每个元素调用给定的函数,将其简化为单个元素,同时保持累加器。

返回一个元组,其中第一个元素是映射的可枚举对象,第二个元素是最终累加器。

该函数 fun 接收两个参数:第一个参数是元素,第二个参数是累加器。 fun 必须返回一个包含两个元素的元组,形式为 {result, accumulator}

对于映射,第一个元组元素必须是 {key, value} 元组。

示例

iex> Enum.map_reduce([1, 2, 3], 0, fn x, acc -> {x * 2, x + acc} end)
{[2, 4, 6], 6}
链接到此函数

max(enumerable, sorter \\ &>=/2, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec max(t(), (element(), element() -> boolean()) | module(), (-> empty_result)) ::
  element() | empty_result
when empty_result: any()

根据 Erlang 的项排序返回 enumerable 中的最大元素。

默认情况下,比较使用 >= 排序函数进行。如果多个元素被认为是最大值,则返回第一个找到的元素。如果您希望返回最后一个被认为是最大值的元素,则排序函数不应该对相等元素返回真值。

如果可枚举对象为空,则调用提供的 empty_fallback。默认的 empty_fallback 会引发 Enum.EmptyError

示例

iex> Enum.max([1, 2, 3])
3

此函数使用 Erlang 的项排序的事实意味着比较是结构性的,而不是语义性的。例如

iex> Enum.max([~D[2017-03-31], ~D[2017-04-01]])
~D[2017-03-31]

在上面的示例中,max/2 返回了 3 月 31 日而不是 4 月 1 日,因为结构比较在比较年份之前比较了日期。出于这个原因,大多数结构都提供了“比较”函数,例如 Date.compare/2,它接收两个结构并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果您将模块作为排序函数传递,Elixir 将自动使用该模块的 compare/2 函数

iex> Enum.max([~D[2017-03-31], ~D[2017-04-01]], Date)
~D[2017-04-01]

最后,如果您不想在可枚举对象为空时引发异常,您可以传递空回退

iex> Enum.max([], &>=/2, fn -> 0 end)
0
链接到此函数

max_by(enumerable, fun, sorter \\ &>=/2, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec max_by(
  t(),
  (element() -> any()),
  (element(), element() -> boolean()) | module(),
  (-> empty_result)
) :: element() | empty_result
when empty_result: any()

返回 enumerable 中由给定 fun 计算的最大元素。

默认情况下,比较使用 >= 排序函数进行。如果多个元素被认为是最大值,则返回第一个找到的元素。如果您希望返回最后一个被认为是最大值的元素,则排序函数不应该对相等元素返回真值。

如果 enumerable 为空,则调用提供的 empty_fallback 函数并返回其值。默认的 empty_fallback 会引发 Enum.EmptyError

示例

iex> Enum.max_by(["a", "aa", "aaa"], fn x -> String.length(x) end)
"aaa"

iex> Enum.max_by(["a", "aa", "aaa", "b", "bbb"], &String.length/1)
"aaa"

此函数使用 Erlang 的项排序的事实意味着比较是结构性的,而不是语义性的。因此,如果您想比较结构,大多数结构都提供了“比较”函数,例如 Date.compare/2,它接收两个结构并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果您将模块作为排序函数传递,Elixir 将自动使用该模块的 compare/2 函数

iex> users = [
...>   %{name: "Ellis", birthday: ~D[1943-05-11]},
...>   %{name: "Lovelace", birthday: ~D[1815-12-10]},
...>   %{name: "Turing", birthday: ~D[1912-06-23]}
...> ]
iex> Enum.max_by(users, &(&1.birthday), Date)
%{name: "Ellis", birthday: ~D[1943-05-11]}

最后,如果您不想在可枚举对象为空时引发异常,您可以传递空回退

iex> Enum.max_by([], &String.length/1, fn -> nil end)
nil
链接到此函数

member?(enumerable, element)

查看源代码
@spec member?(t(), element()) :: boolean()

检查 element 是否存在于 enumerable 中。

成员资格使用匹配(===/2)运算符进行测试。

示例

iex> Enum.member?(1..10, 5)
true
iex> Enum.member?(1..10, 5.0)
false

iex> Enum.member?([1.0, 2.0, 3.0], 2)
false
iex> Enum.member?([1.0, 2.0, 3.0], 2.000)
true

iex> Enum.member?([:a, :b, :c], :d)
false

在保护语句之外调用时,innot in 运算符通过使用此函数来工作。

链接到此函数

min(enumerable, sorter \\ &<=/2, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec min(t(), (element(), element() -> boolean()) | module(), (-> empty_result)) ::
  element() | empty_result
when empty_result: any()

根据 Erlang 的项排序返回 enumerable 中的最小元素。

默认情况下,比较使用 <= 排序函数进行。如果多个元素被认为是最小值,则返回第一个找到的元素。如果您希望返回最后一个被认为是最小值的元素,则排序函数不应该对相等元素返回真值。

如果可枚举对象为空,则调用提供的 empty_fallback。默认的 empty_fallback 会引发 Enum.EmptyError

示例

iex> Enum.min([1, 2, 3])
1

此函数使用 Erlang 的项排序的事实意味着比较是结构性的,而不是语义性的。例如

iex> Enum.min([~D[2017-03-31], ~D[2017-04-01]])
~D[2017-04-01]

在上面的示例中,min/2 返回了 4 月 1 日而不是 3 月 31 日,因为结构比较在比较年份之前比较了日期。出于这个原因,大多数结构都提供了“比较”函数,例如 Date.compare/2,它接收两个结构并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果您将模块作为排序函数传递,Elixir 将自动使用该模块的 compare/2 函数

iex> Enum.min([~D[2017-03-31], ~D[2017-04-01]], Date)
~D[2017-03-31]

最后,如果您不想在可枚举对象为空时引发异常,您可以传递空回退

iex> Enum.min([], fn -> 0 end)
0
链接到此函数

min_by(enumerable, fun, sorter \\ &<=/2, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec min_by(
  t(),
  (element() -> any()),
  (element(), element() -> boolean()) | module(),
  (-> empty_result)
) :: element() | empty_result
when empty_result: any()

返回 enumerable 中由给定 fun 计算的最小元素。

默认情况下,比较使用 <= 排序函数进行。如果多个元素被认为是最小值,则返回第一个找到的元素。如果您希望返回最后一个被认为是最小值的元素,则排序函数不应该对相等元素返回真值。

如果 enumerable 为空,则调用提供的 empty_fallback 函数并返回其值。默认的 empty_fallback 会引发 Enum.EmptyError

示例

iex> Enum.min_by(["a", "aa", "aaa"], fn x -> String.length(x) end)
"a"

iex> Enum.min_by(["a", "aa", "aaa", "b", "bbb"], &String.length/1)
"a"

此函数使用 Erlang 的项排序的事实意味着比较是结构性的,而不是语义性的。因此,如果您想比较结构,大多数结构都提供了“比较”函数,例如 Date.compare/2,它接收两个结构并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果您将模块作为排序函数传递,Elixir 将自动使用该模块的 compare/2 函数

iex> users = [
...>   %{name: "Ellis", birthday: ~D[1943-05-11]},
...>   %{name: "Lovelace", birthday: ~D[1815-12-10]},
...>   %{name: "Turing", birthday: ~D[1912-06-23]}
...> ]
iex> Enum.min_by(users, &(&1.birthday), Date)
%{name: "Lovelace", birthday: ~D[1815-12-10]}

最后,如果您不想在可枚举对象为空时引发异常,您可以传递空回退

iex> Enum.min_by([], &String.length/1, fn -> nil end)
nil
链接到此函数

min_max(enumerable, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec min_max(t(), (-> empty_result)) :: {element(), element()} | empty_result
when empty_result: any()

返回一个元组,其中包含可枚举对象中根据 Erlang 的项排序的最小和最大元素。

如果多个元素被认为是最大值或最小值,则返回第一个找到的元素。

如果 enumerable 为空,则调用提供的 empty_fallback 函数并返回其值。默认的 empty_fallback 会引发 Enum.EmptyError

示例

iex> Enum.min_max([2, 3, 1])
{1, 3}

iex> Enum.min_max([], fn -> {nil, nil} end)
{nil, nil}
链接到此函数

min_max_by(enumerable, fun, sorter_or_empty_fallback \\ &</2, empty_fallback \\ fn -> raise Enum.EmptyError end)

查看源代码
@spec min_max_by(
  t(),
  (element() -> any()),
  (element(), element() -> boolean()) | module(),
  (-> empty_result)
) :: {element(), element()} | empty_result
when empty_result: any()

返回一个元组,其中包含可枚举对象中由给定函数计算的最小和最大元素。

如果多个元素被认为是最大值或最小值,则返回第一个找到的元素。

示例

iex> Enum.min_max_by(["aaa", "bb", "c"], fn x -> String.length(x) end)
{"c", "aaa"}

iex> Enum.min_max_by(["aaa", "a", "bb", "c", "ccc"], &String.length/1)
{"a", "aaa"}

iex> Enum.min_max_by([], &String.length/1, fn -> {nil, nil} end)
{nil, nil}

此函数使用 Erlang 的项排序的事实意味着比较是结构性的,而不是语义性的。因此,如果您想比较结构,大多数结构都提供了“比较”函数,例如 Date.compare/2,它接收两个结构并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果您将模块作为排序函数传递,Elixir 将自动使用该模块的 compare/2 函数

iex> users = [
...>   %{name: "Ellis", birthday: ~D[1943-05-11]},
...>   %{name: "Lovelace", birthday: ~D[1815-12-10]},
...>   %{name: "Turing", birthday: ~D[1912-06-23]}
...> ]
iex> Enum.min_max_by(users, &(&1.birthday), Date)
{
  %{name: "Lovelace", birthday: ~D[1815-12-10]},
  %{name: "Ellis", birthday: ~D[1943-05-11]}
}

最后,如果您不想在可枚举对象为空时引发异常,您可以传递空回退

iex> Enum.min_max_by([], &String.length/1, fn -> nil end)
nil
链接到此函数

product(enumerable)

查看源代码 (自 1.12.0 起)
@spec product(t()) :: number()

返回所有元素的乘积。

如果 enumerable 包含非数值,则引发 ArithmeticError

示例

iex> Enum.product([])
1
iex> Enum.product([2, 3, 4])
24
iex> Enum.product([2.0, 3.0, 4.0])
24.0
@spec random(t()) :: element()

返回 enumerable 的随机元素。

如果 enumerable 为空,则引发 Enum.EmptyError

此函数使用 Erlang 的 :rand 模块 来计算随机值。检查其文档以设置不同的随机算法或不同的种子。

该实现基于 蓄水池抽样 算法。它假设返回的样本可以放入内存;输入 enumerable 不需要,因为它只遍历一次。

如果将范围传递给函数,则此函数将在范围限制之间选择一个随机值,而无需遍历整个范围(因此以恒定时间和恒定内存执行)。

示例

以下示例使用 :exsss 伪随机算法,因为它是 Erlang/OTP 22 中的默认算法

# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exsss, {100, 101, 102})
iex> Enum.random([1, 2, 3])
2
iex> Enum.random([1, 2, 3])
1
iex> Enum.random(1..1_000)
309
链接到此函数

reduce(enumerable, fun)

查看源代码
@spec reduce(t(), (element(), acc() -> acc())) :: acc()

enumerable 中的每个元素调用 fun,并使用累加器。

如果 enumerable 为空,则引发 Enum.EmptyError

enumerable 的第一个元素用作累加器的初始值。然后,函数将使用下一个元素和累加器调用。函数返回的结果用作下次迭代的累加器,递归地进行。当 enumerable 完成时,将返回最后一个累加器。

由于 enumerable 的第一个元素用作累加器的初始值,因此 fun 仅执行 n - 1 次,其中 n 是 enumerable 的长度。对于只有一个元素的 enumerable,此函数不会调用指定函数。

如果你希望使用其他值作为累加器,请使用 Enum.reduce/3.

示例

iex> Enum.reduce([1, 2, 3, 4], fn x, acc -> x * acc end)
24
链接到此函数

reduce(enumerable, acc, fun)

查看源代码
@spec reduce(t(), acc(), (element(), acc() -> acc())) :: acc()

enumerable 中的每个元素调用 fun,并使用累加器。

累加器的初始值为 acc。对于 enumerable 中的每个元素,都会使用累加器调用该函数。函数返回的结果用作下次迭代的累加器。该函数返回最后一个累加器。

示例

iex> Enum.reduce([1, 2, 3], 0, fn x, acc -> x + acc end)
6

iex> Enum.reduce(%{a: 2, b: 3, c: 4}, 0, fn {_key, val}, acc -> acc + val end)
9

Reduce 作为构建块

Reduce(有时称为 fold)是函数式编程中的基本构建块。 Enum 模块中的几乎所有函数都可以基于 reduce 实现。这些函数通常依赖于其他操作,例如 Enum.reverse/1,这些操作已由运行时进行了优化。

例如,我们可以使用 reduce/3 来实现 map/2,如下所示

def my_map(enumerable, fun) do
  enumerable
  |> Enum.reduce([], fn x, acc -> [fun.(x) | acc] end)
  |> Enum.reverse()
end

在上面的示例中,Enum.reduce/3 将每次对 fun 的调用的结果累积到一个列表中,并以相反的顺序排列,最后通过调用 Enum.reverse/1 对列表进行排序。

实现 map/2filter/2 等函数是一个很好的练习,可以帮助你理解 Enum.reduce/3 的强大功能。当一个操作无法通过 Enum 模块中的任何函数来表示时,开发人员很可能会使用 reduce/3

链接到此函数

reduce_while(enumerable, acc, fun)

查看源代码
@spec reduce_while(t(), any(), (element(), any() -> {:cont, any()} | {:halt, any()})) ::
  any()

减少 enumerable,直到 fun 返回 {:halt, term}

fun 的返回值应为

  • {:cont, acc} 用于继续使用 acc 作为新累加器进行归约,或者
  • {:halt, acc} 用于停止归约

如果 fun 返回 {:halt, acc},则归约将停止,并且该函数将返回 acc。否则,如果 enumerable 已耗尽,则该函数将返回最后一个 {:cont, acc} 的累加器。

示例

iex> Enum.reduce_while(1..100, 0, fn x, acc ->
...>   if x < 5 do
...>     {:cont, acc + x}
...>   else
...>     {:halt, acc}
...>   end
...> end)
10
iex> Enum.reduce_while(1..100, 0, fn x, acc ->
...>   if x > 0 do
...>     {:cont, acc + x}
...>   else
...>     {:halt, acc}
...>   end
...> end)
5050
链接到此函数

reject(enumerable, fun)

查看源代码
@spec reject(t(), (element() -> as_boolean(term()))) :: list()

返回 enumerable 中的元素列表,不包括函数 fun 返回真值的那些元素。

另请参见 filter/2.

示例

iex> Enum.reject([1, 2, 3], fn x -> rem(x, 2) == 0 end)
[1, 3]
@spec reverse(t()) :: list()

返回一个列表,其中包含 enumerable 中的元素,按相反顺序排列。

示例

iex> Enum.reverse([1, 2, 3])
[3, 2, 1]
链接到此函数

reverse(enumerable, tail)

查看源代码
@spec reverse(t(), t()) :: list()

反转 enumerable 中的元素,追加 tail,并将其作为列表返回。

这是对 enumerable |> Enum.reverse() |> Enum.concat(tail) 的优化。

示例

iex> Enum.reverse([1, 2, 3], [4, 5, 6])
[3, 2, 1, 4, 5, 6]
链接到此函数

reverse_slice(enumerable, start_index, count)

查看源代码
@spec reverse_slice(t(), non_neg_integer(), non_neg_integer()) :: list()

反转 enumerable 中从初始 start_index 开始到 count 个元素的范围内的元素。

如果 count 大于 enumerable 剩余部分的大小,则此函数将反转 enumerable 的剩余部分。

示例

iex> Enum.reverse_slice([1, 2, 3, 4, 5, 6], 2, 4)
[1, 2, 6, 5, 4, 3]
@spec scan(t(), (element(), any() -> any())) :: list()

将给定的函数应用于 enumerable 中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用 enumerable 中的第一个元素作为起始值。

示例

iex> Enum.scan(1..5, &(&1 + &2))
[1, 3, 6, 10, 15]
链接到此函数

scan(enumerable, acc, fun)

查看源代码
@spec scan(t(), any(), (element(), any() -> any())) :: list()

将给定的函数应用于 enumerable 中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用给定的 acc 作为起始值。

示例

iex> Enum.scan(1..5, 0, &(&1 + &2))
[1, 3, 6, 10, 15]
@spec shuffle(t()) :: list()

返回一个列表,其中包含 enumerable 的元素,已洗牌。

此函数使用 Erlang 的 :rand 模块 来计算随机值。检查其文档以设置不同的随机算法或不同的种子。

示例

以下示例使用 :exsss 伪随机算法,因为它是 Erlang/OTP 22 中的默认算法

# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exsss, {1, 2, 3})
iex> Enum.shuffle([1, 2, 3])
[3, 2, 1]
iex> Enum.shuffle([1, 2, 3])
[2, 1, 3]
链接到此函数

slice(enumerable, index_range)

查看源代码 (自 1.6.0 起)
@spec slice(t(), Range.t()) :: list()

通过 index_range 返回给定 enumerable 的子集列表。

index_range 必须是 Range。对于给定的 enumerable,它会丢弃 index_range.first(以零为基准)之前的元素,然后获取元素直到元素 index_range.last(包含)。

索引已标准化,这意味着负索引将从末尾开始计算(例如,-1 表示 enumerable 的最后一个元素)。

如果 index_range.last 超出边界,则将其分配为最后一个元素的索引。

如果标准化的 index_range.first 超出给定 enumerable 的边界,或者大于标准化的 index_range.last,则返回 []

如果 index_range 中使用了步长 n(不等于 1),则它会从 index_range.firstindex_range.last(根据上述相同规则)获取每隔 n 个元素。

示例

iex> Enum.slice([1, 2, 3, 4, 5], 1..3)
[2, 3, 4]

iex> Enum.slice([1, 2, 3, 4, 5], 3..10)
[4, 5]

# Last three elements (negative indexes)
iex> Enum.slice([1, 2, 3, 4, 5], -3..-1)
[3, 4, 5]

对于 start > stop 的范围,你需要显式地将它们标记为递增

iex> Enum.slice([1, 2, 3, 4, 5], 1..-2//1)
[2, 3, 4]

步长可以是任何正数。例如,要获取集合中的每隔 2 个元素

iex> Enum.slice([1, 2, 3, 4, 5], 0..-1//2)
[1, 3, 5]

要获取前十个元素中的每隔三个元素

iex> integers = Enum.to_list(1..20)
iex> Enum.slice(integers, 0..9//3)
[1, 4, 7, 10]

如果第一个位置在 enumerable 的末尾之后或在范围的最后一个位置之后,它将返回一个空列表

iex> Enum.slice([1, 2, 3, 4, 5], 6..10)
[]

# first is greater than last
iex> Enum.slice([1, 2, 3, 4, 5], 6..5//1)
[]
链接到此函数

slice(enumerable, start_index, amount)

查看源代码
@spec slice(t(), index(), non_neg_integer()) :: list()

返回给定 enumerable 的子集列表,从 start_index(从零开始)开始,包含 amount 个元素(如果有)。

对于给定的 enumerable,它会丢弃 start_index 之前的元素;然后,它会获取 amount 个元素,如果元素不足,则返回尽可能多的元素。

可以传递一个负的 start_index,这意味着 enumerable 会被枚举一次,并且索引从末尾开始计算(例如,-1 从最后一个元素开始切片)。

如果 amount0start_index 超出边界,则返回 []

示例

iex> Enum.slice(1..100, 5, 10)
[6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

# amount to take is greater than the number of elements
iex> Enum.slice(1..10, 5, 100)
[6, 7, 8, 9, 10]

iex> Enum.slice(1..10, 5, 0)
[]

# using a negative start index
iex> Enum.slice(1..10, -6, 3)
[5, 6, 7]
iex> Enum.slice(1..10, -11, 5)
[1, 2, 3, 4, 5]

# out of bound start index
iex> Enum.slice(1..10, 10, 5)
[]
链接到此函数

slide(enumerable, range_or_single_index, insertion_index)

查看源代码 (自 1.13.0 起)
@spec slide(t(), Range.t() | index(), index()) :: list()

range_or_single_index 给出的单个或多个元素从 enumerable 滑动到 insertion_index

要移动的范围的语义与 Enum.slice/2 的语义相匹配。具体来说,这意味着

  • 索引已标准化,这意味着负索引将从末尾开始计算(例如,-1 表示 enumerable 的最后一个元素)。这将导致在列表等类型上进行你的 enumerable 的两次遍历,这些类型不提供恒定时间计数。

  • 如果标准化索引范围的 last 超出边界,则范围将被截断为最后一个元素。

  • 如果标准化索引范围的 first 超出边界,则选择的滑动范围将为空,因此你将获得你的输入列表。

  • 递减范围(例如 5..0//1)也会选择一个要移动的空范围,因此你将获得你的输入列表。

  • 步长不为 1 的范围将引发错误。

示例

# Slide a single element
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 5, 1)
[:a, :f, :b, :c, :d, :e, :g]

# Slide a range of elements backward
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 3..5, 1)
[:a, :d, :e, :f, :b, :c, :g]

# Slide a range of elements forward
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 1..3, 5)
[:a, :e, :f, :b, :c, :d, :g]

# Slide with negative indices (counting from the end)
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 3..-1//1, 2)
[:a, :b, :d, :e, :f, :g, :c]
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], -4..-2, 1)
[:a, :d, :e, :f, :b, :c, :g]

# Insert at negative indices (counting from the end)
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 3, -1)
[:a, :b, :c, :e, :f, :g, :d]
@spec sort(t()) :: list()

根据 Erlang 的项排序对 enumerable 进行排序。

此函数使用归并排序算法。不要使用此函数对结构体进行排序,有关更多信息,请参见 sort/2

示例

iex> Enum.sort([3, 2, 1])
[1, 2, 3]
链接到此函数

sort(enumerable, sorter)

查看源代码
@spec sort(
  t(),
  (element(), element() -> boolean())
  | :asc
  | :desc
  | module()
  | {:asc | :desc, module()}
) :: list()

根据给定的函数对 enumerable 进行排序。

此函数使用归并排序算法。给定的函数应该比较两个参数,如果第一个参数先于或与第二个参数处于相同位置,则返回 true

示例

iex> Enum.sort([1, 2, 3], &(&1 >= &2))
[3, 2, 1]

只要给定函数对被认为相等的数值返回 true,排序算法就会保持稳定

iex> Enum.sort(["some", "kind", "of", "monster"], &(byte_size(&1) <= byte_size(&2)))
["of", "some", "kind", "monster"]

如果函数对相等的值没有返回 true,则排序不稳定,相等项的顺序可能会被洗牌。例如

iex> Enum.sort(["some", "kind", "of", "monster"], &(byte_size(&1) < byte_size(&2)))
["of", "kind", "some", "monster"]

升序和降序(自 v1.10.0 起)

sort/2 允许开发人员传递 :asc:desc 作为排序器,这是对 &<=/2&>=/2 的简化表示。

iex> Enum.sort([2, 3, 1], :asc)
[1, 2, 3]
iex> Enum.sort([2, 3, 1], :desc)
[3, 2, 1]

对结构体进行排序

在对结构体进行排序时,不要使用 </2<=/2>/2>=/2 等运算符。这是因为上面的内置运算符执行结构比较,而不是语义比较。假设我们要对以下日期列表进行排序

iex> dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]]
iex> Enum.sort(dates)
[~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]]

请注意,返回的结果不正确,因为 sort/1 默认使用 <=/2,它会比较它们的结构。当比较结构时,字段按字母顺序比较,这意味着上面的日期将按 daymonth 然后 year 的顺序进行比较,这与我们想要的结果相反。

出于这个原因,大多数结构体都提供了一个“compare”函数,例如 Date.compare/2,它接收两个结构体并返回 :lt(小于)、:eq(等于)和 :gt(大于)。如果你传递一个模块作为排序函数,Elixir 将自动使用该模块的 compare/2 函数

iex> dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]]
iex> Enum.sort(dates, Date)
[~D[2019-01-01], ~D[2019-06-06], ~D[2020-03-02]]

要按降序检索所有日期,你可以将模块包装在一个元组中,并将 :asc:desc 作为第一个元素

iex> dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]]
iex> Enum.sort(dates, {:asc, Date})
[~D[2019-01-01], ~D[2019-06-06], ~D[2020-03-02]]
iex> dates = [~D[2019-01-01], ~D[2020-03-02], ~D[2019-06-06]]
iex> Enum.sort(dates, {:desc, Date})
[~D[2020-03-02], ~D[2019-06-06], ~D[2019-01-01]]
链接到此函数

sort_by(enumerable, mapper, sorter \\ :asc)

查看源代码
@spec sort_by(
  t(),
  (element() -> mapped_element),
  (element(), element() -> boolean())
  | :asc
  | :desc
  | module()
  | {:asc | :desc, module()}
) :: list()
when mapped_element: element()

根据提供的 sorter 函数对 enumerable 的映射结果进行排序。

此函数使用提供的 mapper 函数映射 enumerable 的每个元素。然后,enumerable 会使用 sorter 对映射后的元素进行排序,sorter 默认值为 :asc,并按升序对元素进行排序。

sort_by/3sort/2 的区别在于,它只对 enumerable 中的每个元素计算一次比较值,而不是在每次比较时对每个元素计算一次。如果对两个元素都调用了同一个函数,使用 sort_by/3 更有效率。

升序和降序(自 v1.10.0 起)

sort_by/3 允许开发人员传递 :asc:desc 作为排序器,这相当于使用 &<=/2&>=/2

iex> Enum.sort_by([2, 3, 1], &(&1), :asc)
[1, 2, 3]

iex> Enum.sort_by([2, 3, 1], &(&1), :desc)
[3, 2, 1]

示例

使用默认的 sorter,即 :asc

iex> Enum.sort_by(["some", "kind", "of", "monster"], &byte_size/1)
["of", "some", "kind", "monster"]

按多个属性排序 - 首先按大小排序,然后按首字母排序(这利用了元组按元素逐一比较的事实)

iex> Enum.sort_by(["some", "kind", "of", "monster"], &{byte_size(&1), String.first(&1)})
["of", "kind", "some", "monster"]

类似于 sort/2,您可以传递自定义排序器

iex> Enum.sort_by(["some", "kind", "of", "monster"], &byte_size/1, :desc)
["monster", "some", "kind", "of"]

sort/2 一样,避免使用默认排序函数对结构体进行排序,因为默认情况下它执行结构比较而不是语义比较。在这种情况下,您应该将排序函数作为第三个元素或任何实现 compare/2 函数的模块传递。例如,要按生日升序和降序对用户进行排序,分别如下所示

iex> users = [
...>   %{name: "Ellis", birthday: ~D[1943-05-11]},
...>   %{name: "Lovelace", birthday: ~D[1815-12-10]},
...>   %{name: "Turing", birthday: ~D[1912-06-23]}
...> ]
iex> Enum.sort_by(users, &(&1.birthday), Date)
[
  %{name: "Lovelace", birthday: ~D[1815-12-10]},
  %{name: "Turing", birthday: ~D[1912-06-23]},
  %{name: "Ellis", birthday: ~D[1943-05-11]}
]
iex> Enum.sort_by(users, &(&1.birthday), {:desc, Date})
[
  %{name: "Ellis", birthday: ~D[1943-05-11]},
  %{name: "Turing", birthday: ~D[1912-06-23]},
  %{name: "Lovelace", birthday: ~D[1815-12-10]}
]

性能特征

如第一部分所述,sort_by/3 对可枚举中的每个元素计算一次比较值,而不是在每次比较中计算一次。这意味着 sort_by/3 必须对数据进行初始遍历以计算这些值。

但是,如果这些值的计算成本很低,例如,您已经将要排序的字段提取到一个元组中,那么这些额外的遍历就会变成开销。在这种情况下,请考虑使用 List.keysort/3 代替。

让我们来看一个例子。假设您有一个产品列表和一个 ID 列表。您希望保留所有包含在给定 ID 中的产品,并按价格排序返回其名称。您可以这样写

for(
  product <- products,
  product.id in ids,
  do: product
)
|> Enum.sort_by(& &1.price)
|> Enum.map(& &1.name)

但是,您也可以这样写

for(
  product <- products,
  product.id in ids,
  do: {product.name, product.price}
)
|> List.keysort(1)
|> Enum.map(&elem(&1, 0))

对于性能敏感的代码,使用 List.keysort/3 是更好的选择,因为它避免了额外的遍历。

链接到此函数

split(enumerable, count)

查看源代码
@spec split(t(), integer()) :: {list(), list()}

enumerable 分割成两个可枚举对象,第一个可枚举对象包含 count 个元素。

如果 count 是一个负数,它将从可枚举的末尾开始计数,一直到开头。

请注意,负数 count 意味着 enumerable 将被遍历两次:一次计算位置,第二次进行实际拆分。

示例

iex> Enum.split([1, 2, 3], 2)
{[1, 2], [3]}

iex> Enum.split([1, 2, 3], 10)
{[1, 2, 3], []}

iex> Enum.split([1, 2, 3], 0)
{[], [1, 2, 3]}

iex> Enum.split([1, 2, 3], -1)
{[1, 2], [3]}

iex> Enum.split([1, 2, 3], -5)
{[], [1, 2, 3]}
链接到此函数

split_while(enumerable, fun)

查看源代码
@spec split_while(t(), (element() -> as_boolean(term()))) :: {list(), list()}

在第一次 fun 对元素返回假值 (falsenil) 的位置将可枚举对象分割成两个。

它返回一个包含两个元素列表的元组。触发拆分的元素属于第二个列表。

示例

iex> Enum.split_while([1, 2, 3, 4], fn x -> x < 3 end)
{[1, 2], [3, 4]}

iex> Enum.split_while([1, 2, 3, 4], fn x -> x < 0 end)
{[], [1, 2, 3, 4]}

iex> Enum.split_while([1, 2, 3, 4], fn x -> x > 0 end)
{[1, 2, 3, 4], []}
链接到此函数

split_with(enumerable, fun)

View Source (自 1.4.0 起)
@spec split_with(t(), (element() -> as_boolean(term()))) :: {list(), list()}

根据给定的函数 funenumerable 分割成两个列表。

通过对 enumerable 中的每个元素调用 fun,并将每个元素作为其唯一参数,将给定的 enumerable 拆分为两个列表。返回一个元组,其中第一个列表包含对 enumerable 中所有元素应用 fun 返回真值的结果,第二个列表包含所有应用 fun 返回假值(falsenil)的结果。

两个返回列表中的元素顺序与其在原始可枚举中的顺序相同(如果该可枚举是有序的,例如列表)。请参阅下面的示例。

示例

iex> Enum.split_with([5, 4, 3, 2, 1, 0], fn x -> rem(x, 2) == 0 end)
{[4, 2, 0], [5, 3, 1]}

iex> Enum.split_with([a: 1, b: -2, c: 1, d: -3], fn {_k, v} -> v < 0 end)
{[b: -2, d: -3], [a: 1, c: 1]}

iex> Enum.split_with([a: 1, b: -2, c: 1, d: -3], fn {_k, v} -> v > 50 end)
{[], [a: 1, b: -2, c: 1, d: -3]}

iex> Enum.split_with([], fn {_k, v} -> v > 50 end)
{[], []}
@spec sum(t()) :: number()

返回所有元素的总和。

如果 enumerable 包含非数值,则引发 ArithmeticError

示例

iex> Enum.sum([1, 2, 3])
6

iex> Enum.sum(1..10)
55

iex> Enum.sum(1..10//2)
25
链接到此函数

take(enumerable, amount)

查看源代码
@spec take(t(), integer()) :: list()

enumerable 的开头或结尾获取 amount 个元素。

如果给定正数 amount,它将从 enumerable 的开头获取 amount 个元素。

如果给定负数 amount,则将从末尾获取 amount 个元素。enumerable 将被遍历一次以获取正确的索引,其余计算将从末尾执行。

如果 amount 是 0,它将返回 []

示例

iex> Enum.take([1, 2, 3], 2)
[1, 2]

iex> Enum.take([1, 2, 3], 10)
[1, 2, 3]

iex> Enum.take([1, 2, 3], 0)
[]

iex> Enum.take([1, 2, 3], -1)
[3]
链接到此函数

take_every(enumerable, nth)

查看源代码
@spec take_every(t(), non_neg_integer()) :: list()

返回 enumerable 中每隔 nth 个元素的列表,从第一个元素开始。

第一个元素始终包含在内,除非 nth 为 0。

指定每隔 nth 个元素的第二个参数必须是非负整数。

示例

iex> Enum.take_every(1..10, 2)
[1, 3, 5, 7, 9]

iex> Enum.take_every(1..10, 0)
[]

iex> Enum.take_every([1, 2, 3], 1)
[1, 2, 3]
链接到此函数

take_random(enumerable, count)

查看源代码
@spec take_random(t(), non_neg_integer()) :: list()

enumerable 中获取 count 个随机元素。

请注意,此函数将遍历整个 enumerable 以获取随机子列表。

请参阅 random/1,了解有关实现和随机种子的说明。

示例

# Although not necessary, let's seed the random algorithm
iex> :rand.seed(:exsss, {1, 2, 3})
iex> Enum.take_random(1..10, 2)
[3, 1]
iex> Enum.take_random(?a..?z, 5)
'mikel'
链接到此函数

take_while(enumerable, fun)

查看源代码
@spec take_while(t(), (element() -> as_boolean(term()))) :: list()

enumerable 的开头获取元素,直到 fun 返回真值。

示例

iex> Enum.take_while([1, 2, 3], fn x -> x < 3 end)
[1, 2]
@spec to_list(t()) :: [element()]

enumerable 转换为列表。

示例

iex> Enum.to_list(1..3)
[1, 2, 3]
@spec uniq(t()) :: list()

枚举 enumerable,删除所有重复元素。

示例

iex> Enum.uniq([1, 2, 3, 3, 2, 1])
[1, 2, 3]
链接到此函数

uniq_by(enumerable, fun)

查看源代码
@spec uniq_by(t(), (element() -> term())) :: list()

枚举 enumerable,通过删除函数 fun 返回重复元素的元素来完成。

函数 fun 将每个元素映射到一个项。如果两个元素对 fun 的返回值相同,则它们被视为重复元素。

保留每个元素的第一次出现。

示例

iex> Enum.uniq_by([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end)
[{1, :x}, {2, :y}]

iex> Enum.uniq_by([a: {:tea, 2}, b: {:tea, 2}, c: {:coffee, 1}], fn {_, y} -> y end)
[a: {:tea, 2}, c: {:coffee, 1}]
@spec unzip(t()) :: {[element()], [element()]}

zip/2 相反。从给定的 enumerable 中提取二元组,并将它们组合在一起。

它接收一个 enumerable,其元素为两个元素的元组,并返回一个包含两个列表的元组,每个列表分别由每个元组的第一个和第二个元素组成。

除非 enumerable 是或可以转换为一个元素为两个元素元组的列表,并且每个元组正好有两个元素,否则此函数将失败。

示例

iex> Enum.unzip([{:a, 1}, {:b, 2}, {:c, 3}])
{[:a, :b, :c], [1, 2, 3]}
链接到此函数

with_index(enumerable, fun_or_offset \\ 0)

查看源代码
@spec with_index(t(), integer()) :: [{term(), integer()}]
@spec with_index(t(), (element(), index() -> value)) :: [value] when value: any()

返回 enumerable,其中每个元素都与其索引一起包装在元组中。

可以接收函数或整数偏移量。

如果给定 offset,它将从给定的偏移量开始索引,而不是从零开始。

如果给定 function,它将通过对可枚举的每个元素和索引(以零为基准)调用函数来进行索引。

示例

iex> Enum.with_index([:a, :b, :c])
[a: 0, b: 1, c: 2]

iex> Enum.with_index([:a, :b, :c], 3)
[a: 3, b: 4, c: 5]

iex> Enum.with_index([:a, :b, :c], fn element, index -> {index, element} end)
[{0, :a}, {1, :b}, {2, :c}]
链接到此函数

zip(enumerables)

View Source (自 1.4.0 起)
@spec zip(enumerables) :: [tuple()] when enumerables: [t()] | t()

将来自有限的可枚举对象集合的对应元素压缩成元组列表。

一旦给定集合中的任何一个可枚举完成,压缩过程就将结束。

示例

iex> Enum.zip([[1, 2, 3], [:a, :b, :c], ["foo", "bar", "baz"]])
[{1, :a, "foo"}, {2, :b, "bar"}, {3, :c, "baz"}]

iex> Enum.zip([[1, 2, 3, 4, 5], [:a, :b, :c]])
[{1, :a}, {2, :b}, {3, :c}]
链接到此函数

zip(enumerable1, enumerable2)

查看源代码
@spec zip(t(), t()) :: [{any(), any()}]

将来自两个可枚举对象的对应元素压缩成元组列表。

一旦任何一个可枚举完成,压缩过程就将结束。

示例

iex> Enum.zip([1, 2, 3], [:a, :b, :c])
[{1, :a}, {2, :b}, {3, :c}]

iex> Enum.zip([1, 2, 3, 4, 5], [:a, :b, :c])
[{1, :a}, {2, :b}, {3, :c}]
链接到此函数

zip_reduce(enums, acc, reducer)

View Source (自 1.12.0 起)
@spec zip_reduce(t(), acc, ([term()], acc -> acc)) :: acc when acc: term()

对所有给定的可枚举对象进行归约,只要任何可枚举对象为空就停止。

reducer 将接收 2 个参数:一个元素列表(每个枚举一个)和累加器。

实际上,此函数提供的行为可以通过以下方式实现

Enum.reduce(Stream.zip(enums), acc, reducer)

但是 zip_reduce/3 为了方便起见而存在。

示例

iex> enums = [[1, 1], [2, 2], [3, 3]]
...>  Enum.zip_reduce(enums, [], fn elements, acc ->
...>    [List.to_tuple(elements) | acc]
...> end)
[{1, 2, 3}, {1, 2, 3}]

iex> enums = [[1, 2], [a: 3, b: 4], [5, 6]]
...> Enum.zip_reduce(enums, [], fn elements, acc ->
...>   [List.to_tuple(elements) | acc]
...> end)
[{2, {:b, 4}, 6}, {1, {:a, 3}, 5}]
链接到此函数

zip_reduce(left, right, acc, reducer)

View Source (自 1.12.0 起)
@spec zip_reduce(
  t(),
  t(),
  acc,
  (enum1_elem :: term(), enum2_elem :: term(), acc -> acc)
) :: acc
when acc: term()

对两个可枚举对象进行归约,只要任何一个可枚举对象为空就停止。

实际上,此函数提供的行为可以通过以下方式实现

Enum.reduce(Stream.zip(left, right), acc, reducer)

但是 zip_reduce/4 为了方便起见而存在。

示例

iex> Enum.zip_reduce([1, 2], [3, 4], 0, fn x, y, acc -> x + y + acc end)
10

iex> Enum.zip_reduce([1, 2], [3, 4], [], fn x, y, acc -> [x + y | acc] end)
[6, 4]
链接到此函数

zip_with(enumerables, zip_fun)

View Source (自 1.12.0 起)
@spec zip_with(t(), ([term()] -> term())) :: [term()]

将来自有限的可枚举对象集合的对应元素压缩成列表,并在压缩过程中使用 zip_fun 函数对它们进行转换。

来自 enumerables 中每个枚举的第一个元素将被放入一个列表中,然后传递给一元 zip_fun 函数。然后,来自每个枚举的第二个元素被放入一个列表中并传递给 zip_fun,依此类推,直到 enumerables 中的任何一个枚举耗尽元素。

返回一个列表,其中包含所有调用 zip_fun 的结果。

示例

iex> Enum.zip_with([[1, 2], [3, 4], [5, 6]], fn [x, y, z] -> x + y + z end)
[9, 12]

iex> Enum.zip_with([[1, 2], [3, 4]], fn [x, y] -> x + y end)
[4, 6]
链接到此函数

zip_with(enumerable1, enumerable2, zip_fun)

View Source (自 1.12.0 起)
@spec zip_with(t(), t(), (enum1_elem :: term(), enum2_elem :: term() -> term())) :: [
  term()
]

将来自两个可枚举对象的对应元素压缩成列表,并在压缩过程中使用 zip_fun 函数对它们进行转换。

来自每个集合的对应元素依次传递给提供的二元 zip_fun 函数。返回一个列表,其中包含对每对元素调用 zip_fun 的结果。

一旦任何一个可枚举耗尽元素,压缩过程就将结束。

压缩映射

重要的是要记住,压缩本质上依赖于顺序。如果您压缩两个列表,您将依次获取每个列表中索引处的元素。如果我们将两个映射压缩在一起,您可能会认为您将获取左侧映射中的给定键和右侧映射中的匹配键,但没有这种保证,因为映射键是无序的!请考虑以下情况

left =  %{:a => 1, 1 => 3}
right = %{:a => 1, :b => :c}
Enum.zip(left, right)
# [{{1, 3}, {:a, 1}}, {{:a, 1}, {:b, :c}}]

正如您所见,:a 没有与 :a 配对。如果您想要这种效果,您应该使用 Map.merge/3

示例

iex> Enum.zip_with([1, 2], [3, 4], fn x, y -> x + y end)
[4, 6]

iex> Enum.zip_with([1, 2], [3, 4, 5, 6], fn x, y -> x + y end)
[4, 6]

iex> Enum.zip_with([1, 2, 5, 6], [3, 4], fn x, y -> x + y end)
[4, 6]