檢視原始碼 範圍 (Elixir v1.16.2)

範圍表示一個序列,其中包含零、一或多個遞增或遞減的整數,且具有稱為步長的公差。

建立範圍並進行比對最常見的方式是透過 first..lastfirst..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 的遞增範圍。否則,它是一個遞減範圍。不過,請注意,隱含的遞減範圍已被棄用。因此,如果您需要從 31 的遞減範圍,建議改寫成 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 是從 firstlast 的範圍,以 step 的步長遞增,其中 step 必須是正整數,且所有值 v 都必須是 first <= v and v <= last。因此,範圍 10..0//1 是空範圍,因為沒有任何值 v 符合 10 <= v and v <= 0

類似地,遞減範圍 first..last//step 是從 firstlast 的範圍,以 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

你可以直接存取範圍欄位(firstlaststep),但你不應該手動修改或建立範圍。相反地,請使用適當的運算子或 new/2new/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 協定的實作使用僅基於端點的邏輯,不會實體化整個整數清單。

摘要

函式

檢查兩個範圍是否不相交。

建立一個新的範圍。

建立一個具有 step 的新範圍。

將範圍按給定的步數偏移。

傳回 range 的大小。

將範圍分割為二。

將範圍轉換為清單。

類型

@type limit() :: integer()
@type step() :: pos_integer() | neg_integer()
@type t() :: %Range{first: limit(), last: limit(), step: step()}
@type t(first, last) :: %Range{first: first, last: last, step: step()}

函式

連結到此函式

disjoint?(range1, range2)

檢視原始碼 (自 1.8.0 起)
@spec disjoint?(t(), t()) :: boolean()

檢查兩個範圍是否不相交。

範例

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
@spec new(limit(), limit()) :: t()

建立一個新的範圍。

如果 first 小於 last,範圍會從 firstlast 遞增。如果 first 等於 last,範圍會包含一個元素,也就是數字本身。

如果 first 大於 last,範圍會從 firstlast 遞減,儘管這種行為已不建議使用。因此,建議使用 new/3 明確列出步數。

範例

iex> Range.new(-100, 100)
-100..100
連結到此函式

new(first, last, step)

檢視原始碼 (自 1.12.0 起)
@spec new(limit(), limit(), step()) :: t()

建立一個具有 step 的新範圍。

範例

iex> Range.new(-100, 100, 2)
-100..100//2
連結到此函式

shift(arg, steps_to_shift)

檢視原始碼 (自 1.14.0 起)
@spec shift(t(), integer()) :: t()

將範圍按給定的步數偏移。

範例

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
連結到此函式

size(range)

檢視原始碼 (自 1.12.0 起)
@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(range, split)

檢視原始碼 (自 1.15.0 起)
@spec split(t(), integer()) :: {t(), t()}

將範圍分割為二。

它會傳回一個包含兩個元素的元組。

如果 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)
連結到此函式

to_list(arg1)

檢視原始碼 (自 1.15.0 起)
@spec to_list(t()) :: [integer()]

將範圍轉換為清單。

範例

iex> Range.to_list(0..5)
[0, 1, 2, 3, 4, 5]
iex> Range.to_list(-3..0)
[-3, -2, -1, 0]