查看源代码 Range (Elixir v1.16.2)
范围表示一个由零、一个或多个升序或降序整数组成的序列,这些整数具有一个称为步长的公差。
创建和匹配范围的最常见形式是通过 first..last
和 first..last//step
符号,它们自动从 Kernel
导入。
iex> 1 in 1..10
true
iex> 5 in 1..10
true
iex> 10 in 1..10
true
Elixir 中的范围总是包含边界。当定义了步长时,只有与步长匹配的整数才会属于范围。
iex> 5 in 1..10//2
true
iex> 4 in 1..10//2
false
在没有定义步长的情况下定义范围时,步长将根据范围的第一个和最后一个位置来定义。如果 last >= first
,则它将是一个步长为 1 的递增范围。否则,它是一个递减范围。但是请注意,隐式递减范围已弃用。因此,如果您需要从 3
到 1
的递减范围,最好写成 3..1//-1
。
../0
也可以用作创建范围 0..-1//1
的快捷方式,它也被称为全切片范围。
iex> ..
0..-1//1
用例
范围在 Elixir 中通常有两种用途:作为集合或表示另一个数据结构的切片。
范围作为集合
Elixir 中的范围是可枚举的,因此可以与 Enum
模块一起使用。
iex> Enum.to_list(1..3)
[1, 2, 3]
iex> Enum.to_list(3..1//-1)
[3, 2, 1]
iex> Enum.to_list(1..5//2)
[1, 3, 5]
范围也可以只有一个元素。
iex> Enum.to_list(1..1)
[1]
iex> Enum.to_list(1..1//2)
[1]
甚至可能没有任何元素。
iex> Enum.to_list(10..0//1)
[]
iex> Enum.to_list(0..10//-1)
[]
由 ../0
返回的全切片范围是一个空集合。
iex> Enum.to_list(..)
[]
范围作为切片
范围也常用于切片集合。您可以切片字符串或任何可枚举的。
iex> String.slice("elixir", 1..4)
"lixi"
iex> Enum.slice([0, 1, 2, 3, 4, 5], 1..4)
[1, 2, 3, 4]
在这些情况下,范围的第一个和最后一个值被映射到集合中的位置。
如果给出负数,它将映射到从后开始的位置。
iex> String.slice("elixir", 1..-2//1)
"lixi"
iex> Enum.slice([0, 1, 2, 3, 4, 5], 1..-2//1)
[1, 2, 3, 4]
范围 0..-1//1
(由 ../0
返回)将返回完整的集合,这就是为什么它被称为全切片范围。
iex> String.slice("elixir", ..)
"elixir"
iex> Enum.slice([0, 1, 2, 3, 4, 5], ..)
[0, 1, 2, 3, 4, 5]
定义
递增范围 first..last//step
是从 first
到 last
递增 step
的范围,其中 step
必须是正整数,并且所有值 v
必须满足 first <= v and v <= last
。因此,范围 10..0//1
是一个空范围,因为没有值 v
满足 10 <= v and v <= 0
。
类似地,递减范围 first..last//step
是从 first
到 last
递减 step
的范围,其中 step
必须是负整数,并且值 v
必须满足 first >= v and v >= last
。因此,范围 0..10//-1
是一个空范围,因为没有值 v
满足 0 >= v and v >= 10
。
表示
在内部,范围表示为结构体。
iex> range = 1..9//2
1..9//2
iex> first..last//step = range
iex> first
1
iex> last
9
iex> step
2
iex> range.step
2
您可以直接访问范围字段(first
、last
和 step
),但您不应该手动修改或创建范围。相反,请使用适当的操作符或 new/2
和 new/3
。
范围实现了 Enumerable
协议,其中包含所有 Enumerable
回调的内存高效版本。
iex> range = 1..10
1..10
iex> Enum.reduce(range, 0, fn i, acc -> i * i + acc end)
385
iex> Enum.count(range)
10
iex> Enum.member?(range, 11)
false
iex> Enum.member?(range, 8)
true
无论范围的大小,此类函数调用在内存方面都很高效。Enumerable
协议的实现使用仅基于端点的逻辑,并且不会实际生成整个整数列表。
摘要
类型
函数
检查两个范围是否不相交。
示例
iex> Range.disjoint?(1..5, 6..9)
true
iex> Range.disjoint?(5..1, 6..9)
true
iex> Range.disjoint?(1..5, 5..9)
false
iex> Range.disjoint?(1..5, 2..7)
false
计算范围是否不相交时也会考虑步长。
iex> Range.disjoint?(1..10//2, 2..10//2)
true
# First element in common is 29
iex> Range.disjoint?(1..100//14, 8..100//21)
false
iex> Range.disjoint?(57..-1//-14, 8..100//21)
false
iex> Range.disjoint?(1..100//14, 50..8//-21)
false
iex> Range.disjoint?(1..28//14, 8..28//21)
true
# First element in common is 14
iex> Range.disjoint?(2..28//3, 9..28//5)
false
iex> Range.disjoint?(26..2//-3, 29..9//-5)
false
# Starting from the back without alignment
iex> Range.disjoint?(27..11//-3, 30..0//-7)
true
创建一个新的范围。
如果 first
小于 last
,则范围将从 first
递增到 last
。如果 first
等于 last
,则范围将包含一个元素,即该数字本身。
如果 first
大于 last
,则范围将从 first
递减到 last
,尽管此行为已弃用。因此,建议使用 new/3
显式列出步长。
示例
iex> Range.new(-100, 100)
-100..100
使用 step
创建一个新的范围。
示例
iex> Range.new(-100, 100, 2)
-100..100//2
将范围按给定的步数进行偏移。
示例
iex> Range.shift(0..10, 1)
1..11
iex> Range.shift(0..10, 2)
2..12
iex> Range.shift(0..10//2, 2)
4..14//2
iex> Range.shift(10..0//-2, 2)
6..-4//-2
@spec size(t()) :: non_neg_integer()
返回 range
的大小。
示例
iex> Range.size(1..10)
10
iex> Range.size(1..10//2)
5
iex> Range.size(1..10//3)
4
iex> Range.size(1..10//-1)
0
iex> Range.size(10..1)
10
iex> Range.size(10..1//-1)
10
iex> Range.size(10..1//-2)
5
iex> Range.size(10..1//-3)
4
iex> Range.size(10..1//1)
0
将范围拆分为两个。
它返回一个包含两个元素的元组。
如果 split
小于范围中元素的数量,则范围中的第一个元素将包含 split
个条目,而第二个元素将包含所有剩余的条目。
如果 split
大于范围中元素的数量,则元组中的第二个范围将发出零个元素。
示例
递增范围
iex> Range.split(1..5, 2)
{1..2, 3..5}
iex> Range.split(1..5//2, 2)
{1..3//2, 5..5//2}
iex> Range.split(1..5//2, 0)
{1..-1//2, 1..5//2}
iex> Range.split(1..5//2, 10)
{1..5//2, 7..5//2}
递减范围也可以拆分。
iex> Range.split(5..1//-1, 2)
{5..4//-1, 3..1//-1}
iex> Range.split(5..1//-2, 2)
{5..3//-2, 1..1//-2}
iex> Range.split(5..1//-2, 0)
{5..7//-2, 5..1//-2}
iex> Range.split(5..1//-2, 10)
{5..1//-2, -1..1//-2}
空范围保留其属性,但仍返回空范围。
iex> Range.split(2..5//-1, 2)
{2..3//-1, 4..5//-1}
iex> Range.split(2..5//-1, 10)
{2..3//-1, 4..5//-1}
iex> Range.split(5..2//1, 2)
{5..4//1, 3..2//1}
iex> Range.split(5..2//1, 10)
{5..4//1, 3..2//1}
如果拆分的数字为负数,它将从后部拆分。
iex> Range.split(1..5, -2)
{1..3, 4..5}
iex> Range.split(5..1//-1, -2)
{5..3//-1, 2..1//-1}
如果它为负数且大于范围中的元素数量,则元组的第一个元素将是一个空范围。
iex> Range.split(1..5, -10)
{1..0//1, 1..5}
iex> Range.split(5..1//-1, -10)
{5..6//-1, 5..1//-1}
属性
当拆分范围时,将观察到以下属性。假设 split(input)
返回 {left, right}
,我们有
assert input.first == left.first
assert input.last == right.last
assert input.step == left.step
assert input.step == right.step
assert Range.size(input) == Range.size(left) + Range.size(right)
将范围转换为列表。
示例
iex> Range.to_list(0..5)
[0, 1, 2, 3, 4, 5]
iex> Range.to_list(-3..0)
[-3, -2, -1, 0]