檢視原始碼 範圍 (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
傳回的範圍 0..-1//1
會將集合原樣傳回,這就是為什麼它被稱為全切片範圍
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]