Исходный код Диапазон
Диапазоны представляют собой последовательность нуля, одного или нескольких возрастающих или убывающих целых чисел с общим разностным значением, называемым шагом.
Наиболее распространённая форма создания и сопоставления диапазонов — с помощью 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 использует логику, основанную только на конечных точках, и не материализует весь список целых чисел.
Краткое описание
Типы
Функции
- disjoint?(range1, range2)
Проверяет, являются ли два диапазона непересекающимися.
- new(first, last)
Создаёт новый диапазон.
- new(first, last, step)
Создаёт новый диапазон с
step.- shift(arg, steps_to_shift)
Смещает диапазон на заданное количество шагов.
- size(range)
Возвращает размер
range.- split(range, split)
Разделяет диапазон на две части.
- to_list(arg1)
Преобразует диапазон в список.
Типы
limit()Исходный код
@type limit() :: integer()
step()Исходный код
@type step() :: pos_integer() | neg_integer()
t()Исходный код
@type t() :: %Range{first: limit(), last: limit(), step: step()} t(first, last)Исходный код
@type t(first, last) :: %Range{first: first, last: last, step: step()} Функции
disjoint?(range1, range2)Source
@spec disjoint?(t(), t()) :: boolean()
Проверяет, являются ли два диапазона непересекающимися.
Примеры
iex> Range.disjoint?(1..5, 6..9) true iex> Range.disjoint?(5..1//-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
new(first, last)Source
@spec new(limit(), limit()) :: t()
Создаёт новый диапазон.
Если first меньше last, диапазон будет возрастать от first до last. Если first равно last, диапазон будет содержать один элемент, который является самим числом.
Если first больше last, диапазон будет убывать от first до last, хотя это поведение устарело. Поэтому рекомендуется явно указать шаг с new/3.
Примеры
iex> Range.new(-100, 100) -100..100
new(first, last, step)Source
@spec new(limit(), limit(), step()) :: t()
Создаёт новый диапазон с step.
Примеры
iex> Range.new(-100, 100, 2) -100..100//2
shift(arg, steps_to_shift)Source
@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)Source
@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//-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)Source
@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)Source
@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]
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/Range.html