Книга: Swift. Основы разработки приложений под iOS, iPadOS и macOS. 5-е изд. дополненное и переработанное
Назад: 7. Последовательности и коллекции
Дальше: 9. Массивы (Array)

8. Диапазоны (Range)

Знакомство с последовательностями и коллекциями необходимо начинать с диапазонов — специальных типов данных, позволяющих создавать упорядоченные множества последовательных значений. Диапазоны могут быть конечными и бесконечными, ограниченными слева, справа или с двух сторон. Примером диапазона может служить интервал, включающий целочисленные значения от 1 до 100, идущие по порядку (1, 2, 3, ... 99, 100).

Также они позволяют значительно экономить память компьютера, так как для хранения любого множества (пусть даже с миллионами элементов) необходимо указать лишь начальное и конечное значение. Все промежуточные элементы будут рассчитаны автоматически.

Для создания диапазонов используются два вида операторов: полуоткрытый (..<) и закрытый (...).

8.1. Оператор полуоткрытого диапазона

Оператор полуоткрытого диапазона обозначается двумя точками и знаком меньше (..<). Swift предлагает две формы данного оператора: бинарную (оператор размещен между операндами) и префиксную (оператор размещен перед операндом).

Бинарная форма оператора

Данная форма оператора используется между двумя операндами, определяющими границы создаваемого диапазона.

Синтаксис

левая_граница..<правая_граница

• левая_граница: Comparable — первый элемент диапазона, заданный с помощью сопоставимого типа данных.

• правая_граница: Comparable — элемент, следующий за последним элементом диапазона, заданный с помощью сопоставимого типа данных.

Диапазон элементов от левой границы до предшествующего правой границе элемента. В диапазоне 1..<N первый элемент будет 1, а последний — N-1. Начальное значение должно быть меньше или равно конечному. Попытка создать диапазон, например, от 5 до 2 приведет к ошибке.

Пример

var myRange = 1..<500

ПРИМЕЧАНИЕ Обратите внимание, что при описании условных элементов синтаксиса могут быть использованы указатели не только на конкретные типы данных, но и на целые группы типов с помощью имен протоколов. Так, Comparable говорит о том, что использованное значение должно быть сопоставимого типа данных, а Collection — о том, что значение должно быть коллекцией.

Рассмотрим пример использования бинарного оператора полуоткрытого диапазона (листинг 8.1).

Листинг 8.1

let rangeInt = 1..<5

В данном примере создается диапазон целочисленных значений, включающий 1, 2, 3 и 4 (указанное конечное значение 5 исключается), после чего он инициализируется константе rangeInt.

Для создания диапазонов используются собственные типы данных (точно так же, как для создания строковых значений используются типы String и Character). Всего существует 4 типа, описывающих эти упорядоченные множества элементов.

При использовании бинарного оператора полуоткрытого диапазона создается значение типа Range, а если быть полностью точным, то Range<Int>, где Int определяет целочисленный характер элементов диапазона. Так, по аналогии значение типа Range<Float> будет описывать диапазон из чисел с плавающей точкой, а Range<Character> — из символов.

ПРИМЕЧАНИЕ Ранее мы не встречались с типами данных, содержащих в своих названиях угловые скобки. Данный способ описания типа говорит о том, что он является универсальным шаблоном (Generic). В одной из последних глав книги мы подробно рассмотрим их использование в разработке на Swift.

В общем случае благодаря универсальным шаблонам при создании типа (его реализации на Swift) есть возможность определить требования к типу, указываемому в скобках. Так, для типа Range<T> некий тип T должен быть Comparable, то есть сопоставимым.

Как и для значений других типов данных, при объявлении параметра вы можете явно и неявно задавать его тип (листинг 8.2).

Листинг 8.2

// задаем тип данных явно

var someRangeInt: Range<Int> = 1..<10

type(of:someRangeInt) // Range<Int>.Type

// тип данных определен автоматически

// на основании переданного значения (неявно)

var anotherRangeInt = 51..<59

type(of:anotherRangeInt) // Range<Int>.Type

var angeInt: Range<Int> = 1..<10

Как говорилось ранее, диапазон может содержать не только целочисленные значения, но и элементы других типов. В листинге 8.3 показаны примеры создания диапазонов с элементами типа String, Character и Double.

Листинг 8.3

// диапазон с элементами типа String

var rangeString = «a»..<»z»

type(of:rangeString) // Range<String>.Type

// диапазон с элементами типа Character

var rangeChar: Range<Character> = “a”..<”z”

type(of:rangeChar) // Range<Character>.Type

// диапазон с элементами типа Double

var rangeDouble = 1.0..<5.0

type(of:rangeDouble) // Range<Double>.Typevar rangeChar = "a"..<"z"

var anotherRangeChar: Range<Character> = "b"..<"x"

Возможно, вы спросите, с чем связано то, что при передаче "a"..<"z" устанавливается тип элементов String, хотя в них содержится всего один символ. Логично было бы предположить, что тип данных будет определен как Character. В главе про фундаментальные типы данных было рассказано, что при неявном определении типа Swift отдает предпочтение определенным типам (String вместо Character, Double вместо Float, Int вместо Int8). В данном случае происходит точно такая же ситуация: встречая операнд со значением "a", Swift автоматически относит его к строкам, а не к символам.

Кстати, хочу отметить, что вы без каких-либо проблем сможете создать диапазон "aa"..<"zz", где каждый элемент однозначно не Character.

В качестве начального и конечного значения в любых диапазонах можно использовать не только конкретные значения, но и параметры (переменные и константы), которым эти значения инициализированы (листинг 8.3а).

Листинг 8.3а

var firstElement = 10

var lastElement = 18

var myBestRange = firstElement..<lastElement

Префиксная форма оператора

Данная форма оператора используется перед операндом, позволяющим определить правую границу диапазона.

Синтаксис

..<правая_граница

• правая_граница: Comparable — элемент, следующий за последним элементом диапазона, заданный с помощью сопоставимого типа данных.

Диапазон элементов, определяющий только последний элемент диапазона (предшествует указанной правой границе). Левая граница диапазона заранее неизвестна. Так, в диапазоне ..<N первый элемент будет не определен, а последний —  N-1. Данный оператор используется в тех случаях, когда заранее неизвестен первый элемент, но необходимо ограничить диапазон справа.

Пример

var myRange = ..<500

В листинге 8.4 приведен пример использования данной формы оператора.

Листинг 8.4

var oneSideRange = ..<5

type(of: oneSideRange) // PartialRangeUpTo<Int>.Type

Тип данных созданного диапазона — PartialRangeUpTo, а точнее PartialRangeUpTo<Int>, где Int указывает на тип значений элементов интервала. Как и в случае с Range, данный диапазон может содержать значения и других типов данных. В общем случае тип данных диапазона, создаваемого с помощью постфиксной формы, — PartialRangeUpTo<T>, где Tэто сопоставимый (Comparable) тип данных.

8.2. Оператор закрытого диапазона

Оператор закрытого диапазона обозначается в виде трех точек (...). Swift предлагает три формы: бинарная, префиксная (оператор расположен после операнда) и постфиксная.

Бинарная форма оператора

Синтаксис

левая_граница...правая_граница

• левая_граница: Comparable — первый элемент диапазона, заданный с помощью сопоставимого типа данных.

• правая_граница: Comparable — последний элемент диапазона, заданный с помощью сопоставимого типа данных.

Диапазон элементов от левой границы до правой границы, включая концы. В диапазоне 1...N первый элемент будет 1, а последний — N. Начальное значение должно быть меньше или равно конечному. Попытка создать диапазон, например, от 5 до 2 приведет к ошибке.

Пример

var myRange = 1...100

В листинге 8.5 приведен пример использования данной формы оператора.

Листинг 8.5

var fullRange = 1...10

type(of: fullRange) // ClosedRange<Int>.Type

Тип данных диапазона, созданный бинарной формой оператора, — ClosedRange<Int>. Помимо Int, в качестве значений могут использоваться и другие типы данных. В общем случае тип данных диапазона, создаваемого с помощью бинарной формы, — ClosedRange<T>, где T — это сопоставимый (Comparable) тип данных.

Постфиксная форма оператора

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

Синтаксис

левая_граница...

• правая_граница: Comparable — элемент, следующий за последним элементом диапазона, заданный с помощью сопоставимого типа данных.

Диапазон элементов, определяющий только первый элемент диапазона. Правая граница диапазона заранее неизвестна. Таким образом, в диапазоне 1... первый элемент будет 1, а последний заранее не определен.

Пример

var myRange = 10...

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

В листинге 8.6 приведен пример использования постфиксной формы оператора для создания диапазона.

Листинг 8.6

var infRange = 1...

type(of: infRange) // PartialRangeFrom<Int>.Type

Тип данных данного диапазона — PartialRangeFrom<Int>, где, как и в случае с предыдущими типами диапазона, вместо Int могут быть значения и других типов данных. В общем случае тип данных диапазона, создаваемого с помощью бинарной формы, — PartialRangeFrom<T>, где T — это сопоставимый (Comparable) тип данных.

Префиксная форма оператора

Данная форма, подобно префиксному полуоткрытому оператору, определяет только правую границу, но при этом включает ее в диапазон.

Синтаксис

...правая_граница

• правая_граница: Comparable — последний элемент, заданный с помощью сопоставимого типа данных.

Диапазон элементов, определяющий только последний элемент диапазона. Левая граница диапазона заранее неизвестна. Таким образом, в диапазоне ...N первый элемент будет не определен, а последний — N. Данный оператор используется в тех случаях, когда заранее неизвестен первый элемент, но необходимо ограничить диапазон справа.

Пример

var myRange = ...0

Тип данных диапазона, созданного с помощью постфиксного оператора, — PartialRangeThrough<T>, где T — это сопоставимый (Comparable) тип данных.

В будущих главах мы подробно разберем примеры использования рассмотренных операторов.

8.3. Базовые свойства и методы

При работе с диапазонами вы можете использовать большое количество встроенных функциональных возможностей, доступных «из коробки». Разработчики языка позаботились о вас и реализовали все наиболее востребованные механизмы.

При работе с диапазоном, состоящим из целочисленных значений, можно использовать свойство count для определения количества элементов (листинг 8.7).

Листинг 8.7

var intR = 1...10

intR.count // 10

Для определения наличия элемента в диапазоне служит метод contains(_:) (листинг 8.8).

Листинг 8.8

var floatR: ClosedRange<Float> = 1.0...2.0

floatR.contains(1.4) // true

Для определения наличия элементов в диапазоне служит свойство isEmpty, возвращающее значение типа Bool. При этом обратите внимание, что создать пустой диапазон можно только в случае использования оператора полуоткрытого диапазона (..<) с указанием одинаковых операндов (листинг 8.9).

Листинг 8.9

// диапазон без элементов

var emptyR = 0..<0

emptyR.count   // 0

emptyR.isEmpty // true

// диапазон с единственным элементом - 0

var notEmptyR = 0...0

notEmptyR.count   // 1

notEmptyR.isEmpty // false

Свойства lowerBound и upperBound позволяют определить значения левой и правой границы, а методы min() и max() — минимальное и максимальное значения, правда, доступны они только при работе с целочисленными значениями (листинг 8.10).

Листинг 8.10

var anotherIntR = 20..<34

anotherIntR.lowerBound // 20

anotherIntR.upperBound // 34

anotherIntR.min()      // 20

anotherIntR.max()      // 33

Назад: 7. Последовательности и коллекции
Дальше: 9. Массивы (Array)