Источник Диапазон
Диапазоны представляют собой последовательность нуля, одного или нескольких возрастающих или убывающих целых чисел с общим разностью, называемой шагом.
Наиболее распространённый способ создания и сопоставления диапазонов — с помощью 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 использует логику, основанную только на конечных точках, и не материализует весь список целых чисел.
Резюме
Типы
Функции
- 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)
Преобразует диапазон в список.
Типы
Функции
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.18.1/Range.html