查看源代码 Enum (Elixir v1.16.2)
用于处理集合(称为可枚举对象)的函数。
在 Elixir 中,可枚举对象是指任何实现了 Enumerable
协议的数据类型。 List
s ([1, 2, 3]
)、Map
s (%{foo: 1, bar: 2}
) 和 Range
s (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}]
语言中存在许多其他可枚举对象,例如 MapSet
s 和 File.stream!/3
返回的数据类型,它允许像枚举对象一样遍历文件。
有关 Enum
模块中所有函数的概述,请参阅 Enum
速查表。
此模块中的函数以线性时间工作。这意味着执行操作所需的时间与可枚举对象的长度增长速度相同。这在 Enum.map/2
等操作中是预期的。毕竟,如果我们要遍历列表中的每个元素,列表越长,我们需要遍历的元素就越多,花费的时间就越长。
这种线性行为也应该在 count/1
、member?/2
、at/2
等操作中预料到。虽然 Elixir 允许数据类型为这些操作提供高性能变体,但你不应该期望它总是可用,因为 Enum
模块旨在与各种数据类型一起工作,并非所有数据类型都能提供优化行为。
最后,请注意 Enum
模块中的函数是急切的:它们将在被调用时立即遍历可枚举对象。这在处理无限可枚举对象时特别危险。在这种情况下,你应该使用 Stream
模块,它允许你以惰性方式表达计算,而无需遍历集合,并处理可能无限的集合。有关示例和文档,请参阅 Stream
模块。
摘要
函数
如果 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
的大小。
返回 enumerable
中 fun
返回真值的元素个数。
计算可枚举对象,在 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_fun
将 enumerable
分成组。
在枚举的每个元素之间插入 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
对元素返回假值 (false
或 nil
) 的位置将可枚举对象分割成两个。
根据给定的函数 fun
将 enumerable
分割成两个列表。
返回所有元素的总和。
从 enumerable
的开头或结尾获取 amount
个元素。
返回 enumerable
中每隔 nth
个元素的列表,从第一个元素开始。
从 enumerable
中获取 count
个随机元素。
从 enumerable
的开头获取元素,直到 fun
返回真值。
将 enumerable
转换为列表。
枚举 enumerable
,删除所有重复元素。
枚举 enumerable
,通过删除函数 fun
返回重复元素的元素来完成。
与 zip/2
相反。从给定的 enumerable
中提取二元组,并将它们组合在一起。
返回 enumerable
,其中每个元素都与其索引一起包装在元组中。
将来自有限的可枚举对象集合的对应元素压缩成元组列表。
将来自两个可枚举对象的对应元素压缩成元组列表。
对所有给定的可枚举对象进行归约,只要任何可枚举对象为空就停止。
对两个可枚举对象进行归约,只要任何一个可枚举对象为空就停止。
将来自有限的可枚举对象集合的对应元素压缩成列表,并在压缩过程中使用 zip_fun
函数对它们进行转换。
将来自两个可枚举对象的对应元素压缩成列表,并在压缩过程中使用 zip_fun
函数对它们进行转换。
类型
函数
如果 enumerable
中的所有元素都为真值,则返回 true
。
当元素的值为假值 (false
或 nil
) 时,迭代立即停止,并返回 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
曾经返回假值 (false
或 nil
),迭代立即停止,并返回 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
。这是一个对空集合的明确定义的逻辑论证。
如果 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
查找给定 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
在 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]]
@spec chunk_every(t(), pos_integer()) :: [list()]
是 chunk_every(enumerable, count, count)
的快捷方式。
@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]]
@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
使用完成遍历(或 halt
ed)后的最终累加器调用,以处理作为累加器一部分返回但未由 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]]
给定可枚举对象的枚举对象,将 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]
将 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
@spec count(t(), (element() -> as_boolean(term()))) :: non_neg_integer()
返回 enumerable
中 fun
返回真值的元素个数。
示例
iex> Enum.count([1, 2, 3, 4, 5], fn x -> rem(x, 2) == 0 end)
2
@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
@spec count_until(t(), (element() -> as_boolean(term())), pos_integer()) :: non_neg_integer()
计算可枚举对象中 fun
返回真值的元素个数,在 limit
处停止。
有关更多信息,请参阅 count/2
和 count_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
枚举 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]
枚举 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]
从 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]
@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)
[]
@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]
对 enumerable
中的每个元素调用给定的 fun
。
返回 :ok
。
示例
Enum.each(["some", "example"], fn x -> IO.puts(x) end)
"some"
"example"
#=> :ok
确定 enumerable
是否为空。
如果 enumerable
为空,则返回 true
,否则返回 false
。
示例
iex> Enum.empty?([])
true
iex> Enum.empty?([1, 2, 3])
false
查找给定 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
查找给定 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
@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)
返回 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
@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
@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!"
对 enumerable
上的给定 fun
进行映射,并展平结果。
此函数返回一个新的可枚举对象,该对象通过将对 enumerable
的每个元素调用 fun
的结果连接在一起而构建;从概念上讲,这类似于 map/2
和 concat/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]]
@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}
返回一个映射,其键是 enumerable
的唯一元素,其值为每个元素的计数。
示例
iex> Enum.frequencies(~w{ant buffalo ant ant buffalo dingo})
%{"ant" => 3, "buffalo" => 2, "dingo" => 1}
返回一个映射,其键是 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}
根据 key_fun
将 enumerable
分成组。
结果是一个映射,其中每个键由 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"}]
}
在枚举的每个元素之间插入 separator
。
示例
iex> Enum.intersperse([1, 2, 3], 0)
[1, 0, 2, 0, 3]
iex> Enum.intersperse([1], 0)
[1]
iex> Enum.intersperse([], 0)
[]
@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}
@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}
使用 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"
返回一个列表,其中每个元素都是对 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]
@spec map_every(t(), non_neg_integer(), (element() -> any())) :: list()
返回一个列表,该列表包含对 enumerable
的每个 nth
个元素调用 fun
的结果,从第一个元素开始。
第一个元素总是传递给给定的函数,除非 nth
为 0
。
指定每隔 nth
个元素的第二个参数必须是非负整数。
如果 nth
为 0
,则 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]
在一遍中映射和插入给定的可枚举对象。
示例
iex> Enum.map_intersperse([1, 2, 3], :a, &(&1 * 2))
[2, :a, 4, :a, 6]
@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"
对 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}
@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
检查 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
@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
@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
返回所有元素的乘积。
如果 enumerable
包含非数值,则引发 ArithmeticError
。
示例
iex> Enum.product([])
1
iex> Enum.product([2, 3, 4])
24
iex> Enum.product([2.0, 3.0, 4.0])
24.0
返回 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
对 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
对 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/2
、filter/2
等函数是一个很好的练习,可以帮助你理解 Enum.reduce/3
的强大功能。当一个操作无法通过 Enum
模块中的任何函数来表示时,开发人员很可能会使用 reduce/3
。
减少 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
@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]
返回一个列表,其中包含 enumerable
中的元素,按相反顺序排列。
示例
iex> Enum.reverse([1, 2, 3])
[3, 2, 1]
反转 enumerable
中的元素,追加 tail
,并将其作为列表返回。
这是对 enumerable |> Enum.reverse() |> Enum.concat(tail)
的优化。
示例
iex> Enum.reverse([1, 2, 3], [4, 5, 6])
[3, 2, 1, 4, 5, 6]
@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]
将给定的函数应用于 enumerable
中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用 enumerable
中的第一个元素作为起始值。
示例
iex> Enum.scan(1..5, &(&1 + &2))
[1, 3, 6, 10, 15]
将给定的函数应用于 enumerable
中的每个元素,将结果存储在一个列表中,并将其作为下一个计算的累加器传递。使用给定的 acc
作为起始值。
示例
iex> Enum.scan(1..5, 0, &(&1 + &2))
[1, 3, 6, 10, 15]
返回一个列表,其中包含 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]
通过 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.first
到 index_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)
[]
@spec slice(t(), index(), non_neg_integer()) :: list()
返回给定 enumerable
的子集列表,从 start_index
(从零开始)开始,包含 amount
个元素(如果有)。
对于给定的 enumerable
,它会丢弃 start_index
之前的元素;然后,它会获取 amount
个元素,如果元素不足,则返回尽可能多的元素。
可以传递一个负的 start_index
,这意味着 enumerable 会被枚举一次,并且索引从末尾开始计算(例如,-1
从最后一个元素开始切片)。
如果 amount
为 0
或 start_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)
[]
将 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]
根据 Erlang 的项排序对 enumerable
进行排序。
此函数使用归并排序算法。不要使用此函数对结构体进行排序,有关更多信息,请参见 sort/2
。
示例
iex> Enum.sort([3, 2, 1])
[1, 2, 3]
@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
,它会比较它们的结构。当比较结构时,字段按字母顺序比较,这意味着上面的日期将按 day
、month
然后 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]]
@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/3
与 sort/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
是更好的选择,因为它避免了额外的遍历。
将 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]}
在第一次 fun
对元素返回假值 (false
或 nil
) 的位置将可枚举对象分割成两个。
它返回一个包含两个元素列表的元组。触发拆分的元素属于第二个列表。
示例
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], []}
根据给定的函数 fun
将 enumerable
分割成两个列表。
通过对 enumerable
中的每个元素调用 fun
,并将每个元素作为其唯一参数,将给定的 enumerable
拆分为两个列表。返回一个元组,其中第一个列表包含对 enumerable
中所有元素应用 fun
返回真值的结果,第二个列表包含所有应用 fun
返回假值(false
或 nil
)的结果。
两个返回列表中的元素顺序与其在原始可枚举中的顺序相同(如果该可枚举是有序的,例如列表)。请参阅下面的示例。
示例
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)
{[], []}
返回所有元素的总和。
如果 enumerable
包含非数值,则引发 ArithmeticError
。
示例
iex> Enum.sum([1, 2, 3])
6
iex> Enum.sum(1..10)
55
iex> Enum.sum(1..10//2)
25
从 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]
@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]
@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'
@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]
将 enumerable
转换为列表。
示例
iex> Enum.to_list(1..3)
[1, 2, 3]
枚举 enumerable
,删除所有重复元素。
示例
iex> Enum.uniq([1, 2, 3, 3, 2, 1])
[1, 2, 3]
枚举 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}]
与 zip/2
相反。从给定的 enumerable
中提取二元组,并将它们组合在一起。
它接收一个 enumerable
,其元素为两个元素的元组,并返回一个包含两个列表的元组,每个列表分别由每个元组的第一个和第二个元素组成。
除非 enumerable
是或可以转换为一个元素为两个元素元组的列表,并且每个元组正好有两个元素,否则此函数将失败。
示例
iex> Enum.unzip([{:a, 1}, {:b, 2}, {:c, 3}])
{[:a, :b, :c], [1, 2, 3]}
@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}]
将来自有限的可枚举对象集合的对应元素压缩成元组列表。
一旦给定集合中的任何一个可枚举完成,压缩过程就将结束。
示例
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}]
将来自两个可枚举对象的对应元素压缩成元组列表。
一旦任何一个可枚举完成,压缩过程就将结束。
示例
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}]
对所有给定的可枚举对象进行归约,只要任何可枚举对象为空就停止。
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}]
@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_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_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]