obo.dev

Коллекции. Диапазоны (Range)

05 Dec 2022

Диапазон (Range)

Диапазон представляет собой набор значений, который определяется начальной и конечной точкой. Диапазоны бывают полуоткрытые (Range) и закрытые (ClosedRange).

По типу данных относятся к структурам.

Полуоткрытый диапазон (Range) - полуоткрытый интервал от нижней границы до верхней границы, но не включая ее.

Закрытый диапазон (ClosedRange) - интервал от нижней границы до верхней границы включительно.

Экземпляр полуоткрытого диапазона создается с помощью оператора полуоткрытого диапазона (..<):

let underFive = 0.0..<5.0

Экземпляр закрытого диапазона создается с помощью оператора полуоткрытого диапазона (...):

let throughFive = 0...5

Границы диапазона могут иметь отрицательное значение:

// negative lower bound
let rangeOne = -3...1

// negative upper and lower bound
let rangeTwo = -9 ... -2

При этом следует разделять бинарный оператор диапазона (...) от унарного минуса. В этом случае следует отделить пробелами с двух сторон бинарный оператор диапазона (...).

Следует помнить, что нижняя граница диапазона не должна быть больше верхней границы. Это приведет к ошибке:

let throughFive = 3...1 // Fatal error: Range requires lowerBound <= upperBound

Если необходимо использовать диапазон в обратном порядке, следует использовать к нему метод reversed() (см. пример ниже).

Диапазоны имеют следующие свойства и методы:

  • var isEmpty: Bool - логическое значение, указывающее, содержит ли диапазон элементы;
  • let lowerBound: Bound - нижняя граница диапазона;
  • let upperBound: Bound - верхняя граница диапазона;
  • func contains(Bound) -> Bool - Возвращает логическое значение, указывающее, содержится ли данный элемент в пределах диапазона;
  • func clamped(to: Range) -> Range, - Возвращает копию этого диапазона, ограниченную заданным предельным диапазоном.

Использование диапазонов

Можно использовать экземпляр Range или ClosedRange, чтобы быстро проверить, содержится ли значение в определенном диапазоне значений. Следует помнить, что экземпляр Range не содержит верхнюю границу.

Пример:

underFive.contains(3.14)
// true
underFive.contains(6.28)
// false
underFive.contains(5.0)
// false

При этом экземпляр ClosedRange содержит как нижнюю, так и верхнюю границу.

Пример:

throughFive.contains(3)
// true
throughFive.contains(10)
// false
throughFive.contains(5)
// true

Поскольку закрытый диапазон включает верхнюю границу, это значение содержится в закрытом диапазоне, нижняя граница которого равна верхней границе. Поэтому экземпляр ClosedRange не может представлять пустой диапазон.

Пример:

let zeroInclusive = 0...0
zeroInclusive.contains(0)
// true
zeroInclusive.isEmpty
// false

Экземпляры Range могут представлять пустой интервал, в отличие от ClosedRange.

Пример:

let empty = 0.0..<0.0
empty.contains(0.0)
// false
empty.isEmpty
// true

Ограничение диапазона

Метод экземпляра clamped(to:) возвращает копию этого диапазона, ограниченную заданным предельным диапазоном.

  • В качестве параметров передаются пределы - диапазон для ограничения границ этого диапазона.
  • Возвращаемое значение - новый диапазон, зажатый в границах пределов.

Границы результата всегда ограничены границами пределов. Если два диапазона не перекрываются, результатом является одноэлементный диапазон на верхней или нижней границе пределов.

Пример Range (func clamped(to limits: Range<Bound>) -> Range<Bound>):

let x: Range = 0..<20
print(x.clamped(to: 10..<1000))
// Prints "10..<20"
let y: Range = 0..<5
print(y.clamped(to: 10..<1000))
// Prints "10..<10"

Пример ClosedRange (func clamped(to limits: ClosedRange<Bound>) -> ClosedRange<Bound>):

let x: ClosedRange = 0...20
print(x.clamped(to: 10...1000))
// Prints "10...20"
let y: ClosedRange = 0...5
print(y.clamped(to: 10...1000))
// Prints "10...10"

Использование диапазона как коллекции последовательных значений

Когда полуоткрытый диапазон (Range) или закрытый диапазон (ClosedRange) использует целые числа в качестве нижней и верхней границ или любой другой тип, который соответствует протоколу Strideable с целым шагом, можно использовать этот диапазон в цикле for-in или с любым методом последовательности (Sequence) или коллекции (Collection).

Использование диапазона в цикле

Элементы полуоткрытого диапазона — это последовательные значения от его нижней границы до верхней границы, но не включая ее.

Пример:

for n in 3..<5 {
    print(n)
}
// Prints "3"
// Prints "4"

Элементы закрытого диапазона — это последовательные значения от нижней границы до верхней границы включительно.

Пример:

for n in 3...5 {
    print(n)
}
// Prints "3"
// Prints "4"
// Prints "5"

Поскольку типы с плавающей запятой, такие как Float и Double, являются собственными типами Stride, их нельзя использовать в качестве границ счетного диапазона. Если вам нужно выполнить итерацию по последовательным значениям с плавающей запятой, см. функцию stride(from:to:by:).

Метод reversed()

Метод reversed() возвращает перевернутый наоборот диапазон:

let range = 5...8 // 5 6 7 8

for val in range.reversed() {
    print(val)
}
// 8
// 7
// 6
// 5

Метод start(with:)

Метод start(with:) позволяет проверить, начинается ли диапазон с поддиапазона, который передается через параметр with:

let range = 5...8 // 5 6 7 8
 
let st1 = range.starts(with: 1...5) // false
let st2 = range.starts(with: 5...7) // true

Метод overlaps()

Метод overlaps() возвращает true, если два диапазона хотя бы частично совпадают:

let range = 5...8   // 5 6 7 8

range.overlaps(3...9) // true
range.overlaps(9...19) // false

Односторонний диапазон (One-sided Range)

Односторонний диапазон - это отдельный тип данных, который представляет собой диапазон с одной границей. Односторонний диапазон содержит элементы до бесконечности в одном направлении.

Например:

let range1 = ..<2
print(range1)
// PartialRangeUpTo<Int>(upperBound: 2)

let range2 = ...2
print(range2)
// PartialRangeThrough<Int>(upperBound: 2)

Здесь ...<2 — односторонний диапазон. Он содержит все элементы от 2 до -∞.

Точно так же диапазон 2...:

let range3 = 2...
print(range3)
// PartialRangeFrom<Int>(lowerBound: 2)

содержит все элементы от 2 до +∞.

Можно проверить это, проверив, присутствует ли определенное число в диапазоне.

Например:

// one-sided range using 
// ..< operator
let range1 = ..<2

// check if -9 is in the range
print(range1.contains(-1))
// true

// one-sided range using
// ... operator
let range2 = 2...

// check if 33 is in the range
print(range2.contains(33))
// true

В этом примере использовался метод contains(), чтобы проверить, присутствует ли определенное число в диапазоне.

Примечание. При одностороннем диапазоне - устанавливается только верхняя или нижняя граница.

Внимание. Так как односторонний диапазон с одной стороны содержит элементы до бесконечности, то не следует такой диапазон использовать, например цикле в качестве коллекции для перебора.


Еще полезные ссылки

Также информацию по диапазонам можно получить на странице официальной документации (Узнать больше про методы и свойства диапазонов).

Ссылки на официальную документацию: