Знакомство с последовательностями и коллекциями необходимо начинать с диапазонов — специальных типов данных, позволяющих создавать упорядоченные множества последовательных значений. Диапазоны могут быть конечными и бесконечными, ограниченными слева, справа или с двух сторон. Примером диапазона может служить интервал, включающий целочисленные значения от 1 до 100, идущие по порядку (1, 2, 3, ... 99, 100).
Также они позволяют значительно экономить память компьютера, так как для хранения любого множества (пусть даже с миллионами элементов) необходимо указать лишь начальное и конечное значение. Все промежуточные элементы будут рассчитаны автоматически.
Для создания диапазонов используются два вида операторов: полуоткрытый (..<) и закрытый (...).
Оператор полуоткрытого диапазона обозначается двумя точками и знаком меньше (..<). 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) тип данных.
Оператор закрытого диапазона обозначается в виде трех точек (...). 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) тип данных.
В будущих главах мы подробно разберем примеры использования рассмотренных операторов.
При работе с диапазонами вы можете использовать большое количество встроенных функциональных возможностей, доступных «из коробки». Разработчики языка позаботились о вас и реализовали все наиболее востребованные механизмы.
При работе с диапазоном, состоящим из целочисленных значений, можно использовать свойство 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