Исходный код Диапазон
Диапазоны представляют собой последовательность нуля, одного или нескольких возрастающих или убывающих целых чисел с общим разностным шагом.
Наиболее распространённая форма создания и сопоставления диапазонов осуществляется с помощью обозначений first..last и first..last//step, автоматически импортируемых из Kernel:
iex> 1 in 1..10 true iex> 5 in 1..10 true iex> 10 in 1..10 true
Диапазоны всегда включают конечные значения. Если определён шаг, целые числа будут принадлежать диапазону только в том случае, если они соответствуют этому шагу:
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 использует логику, основанную только на конечных значениях, и не материализует весь список целых чисел.
Краткое описание
Типы
Функции
- непересекаются?(диапазон1, диапазон2)
Проверяет, являются ли два диапазона непересекающимися.
- новый(первый, последний)
Создаёт новый диапазон.
- новый(первый, последний, шаг)
Создаёт новый диапазон с
step.- сдвиг(аргумент, сдвиг_на_шагов)
Сдвигает диапазон на заданное количество шагов.
- размер(диапазон)
Возвращает размер
range.- разделить(диапазон, разделить)
Разделяет диапазон на два.
- в_список(арг1)
Преобразует диапазон в список.
Типы
limit()Исходный код
@type limit() :: integer()
шаг()Исходный код
@type step() :: pos_integer() | neg_integer()
t()Исходный код
@type t() :: %Range{first: limit(), last: limit(), step: step()} t(первый, последний)Исходный код
@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, 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) 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)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.16.3/Range.html