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

6. Кортежи (Tuple)

Возможно, вы никогда не встречались в программировании с таким понятием, как кортежи, тем не менее это одно из очень полезных функциональных средств, доступных в Swift. Кортежи, к примеру, могут использоваться для работы с координатами. Согласитесь, куда удобнее орудовать конструкцией (x, y, z), записанной в одну переменную, чем создавать по отдельной переменной для каждой оси координат.

6.1. Основные сведения о кортежах

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

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

Литерал кортежа

Кортеж может быть создан с помощью литерала кортежа.

Синтаксис

(значение_1, значение_2, ..., значение_N)

• значение: Any — очередное значение произвольного типа данных, включаемое в состав кортежа.

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

ПРИМЕЧАНИЕ Далее по тексту будет встречаться большое количество блоков «Синтаксис». В них описывается, каким образом могут быть использованы рассматриваемые конструкции в реальных программах. Прежде чем перейти к данным блокам, необходимо описать структуру этого блока, а также используемые элементы.

Первой строкой всегда будет определяться непосредственно сам синтаксис, описывающий использование конструкции в коде программы. При этом могут использоваться условные элементы (как, например, значение_1, значение_2 и значение_N в синтаксисе, приведенном выше). Обычно они написаны в виде текста, позволяющего понять их назначение.

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

После имени условного элемента может быть указан его тип данных. При этом если в качестве элемента должно быть использовано конкретное значение определенного типа (к примеру, строковое, числовое, логическое и т.д.), то тип отделяется двоеточием (:). Если же в качестве элемента может быть использовано выражение (например, a+b или r > 100), то тип будет указан после тире и правой угловой скобки, изображающих стрелку (->). Может быть определен как один (например, Int) , так и множество типов данных (например, Int, String).

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

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

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

Кортеж хранится в переменных и константах точно так же, как значения фундаментальных типов данных.

Синтаксис

let имяКонстанты = (значение_1, значение_2, ..., значение_N)

var имяПеременной = (значение_1, значение_2, ..., значение_N)

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

Рассмотрим следующий пример. Объявим константу и проинициализируем ей кортеж, состоящий из трех элементов типов: Int, String и Bool (листинг 6.1).

Листинг 6.1

let myProgramStatus = (200, "In Work", true)

myProgramStatus // (.0 200, .1 "In Work", .2 true)

В данном примере myProgramStatus — константа, содержащая в качестве значения кортеж, описывающий статус работы некоей программы и состоящий их трех элементов:

• 200 — целое число типа Int, код состояния программы;

• "In work" — строковый литерал типа String, текстовое описание состояния программы;

• true — логическое значение типа Bool, возможность продолжения функционирования программы.

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

Тип данных кортежа

Но если кортеж группирует значения различных типов данных в одно, то какой же тогда тип данных у самого кортежа и параметра, хранящего его значение?

Тип данных кортежа — это фиксированная упорядоченная последовательность имен типов данных элементов кортежа.

Синтаксис

(имя_типа_данных_элемента_1, имя_типа_данных_элемента_2, ...,

имя_типа_данных_элемента_N)

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

Пример

(Int, Int, Double)

Типом данных кортежа myProgramStatus из листинга 6.1 является (Int, String, Bool). При этом тип данных задан неявно, так как определен автоматически на основании элементов кортежа. Так как порядок указания типов данных должен соответствовать порядку следования элементов в кортеже, тип (Bool, String, Int) является отличным от (Int, String, Bool).

В листинге 6.2 производится сравнение типов данных различных кортежей.

Листинг 6.2

var tuple1 = (200, "In Work", true)

var tuple2 = (true, "On Work", 200)

print( type(of:tuple1) == type(of:tuple2) )

Консоль

false

Для сравнения типов данных кортежей используются значения, возвращаемые глобальной функцией type(of:), определяющей тип переданного в него объекта.

Предположим, что в кортеже myProgramStatus первым элементом вместо целочисленного должно идти значение типа Float. В этом случае можно явно определить тип данных кортежа (через двоеточие после имени параметра) (листинг 6.3).

Листинг 6.3

// объявляем кортеж с явно заданным типом

let floatStatus: (Float, String, Bool) = (200.2, "In Work", true)

floatStatus // (.0 200.2, .1 “In Work”, .2 true)

Вы не ограничены каким-либо определенным количеством элементов кортежа. Кортеж может содержать столько элементов, сколько потребуется (но помните, что не рекомендуется использовать больше семи элементов). В листинге 6.4 приведен пример создания кортежа из четырех элементов одного типа. При этом используется псевдоним типа данных Int, что не запрещается.

Листинг 6.4

// объявляем псевдоним для типа Int

typealias numberType = Int

// объявляем кортеж и инициализируем его значение

let numbersTuple: (Int, Int, numberType, numberType) = (0, 1, 2, 3)

numbersTuple // (.0 0, .1 1, .2 2, .3 3)

6.2. Взаимодействие с элементами кортежа

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

Инициализация значений в параметры

Вы можете одним выражением объявить новые параметры и проинициализировать в них значения всех элементов кортежа. Для этого после ключевого слова var (или let, если объявляете константы) в скобках и через запятую необходимо указать имена новых параметров, а после оператора инициализации передать кортеж. Обратите внимание, что количество объявляемых параметров должно соответствовать количеству элементов кортежа (листинг 6.5).

Листинг 6.5

// записываем значения кортежа в переменные

var (statusCode, statusText, statusConnect) = myProgramStatus

// выводим информацию

print("Код ответа — \(statusCode)")

print("Текст ответа — \(statusText)")

print("Связь с сервером — \(statusConnect)")

Консоль:

Код ответа — 200

Текст ответа — In Work

Связь с сервером — true

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

Листинг 6.6

/* объявляем две переменные с одновременной

инициализацией им значений */

var (myName, myAge) = ("Тролль", 140)

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

print("Мое имя \(myName), и мне \(myAge) лет")

Консоль:

Мое имя Тролль, и мне 140 лет

Переменные myName и myAge инициализированы соответствующими значениями элементов кортежа ("Тролль", 140).

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

Листинг 6.7

/* получаем только необходимые

значения кортежа */

var (statusCode, _, _) = myProgramStatus

В результате в переменную statusCode запишется значение первого элемента кортежа — myProgramStatus. Остальные значения будут проигнорированы.

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

Доступ к элементам кортежа через индексы

Каждый элемент кортежа, помимо значения, содержит целочисленный индекс, который может быть использован для доступа к данному элементу. Индексы всегда расположены по порядку, начиная с нуля. Таким образом, в кортеже из N элементов индекс первого элемента будет 0, а к последнему можно обратиться с помощью индекса N-1.

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

Листинг 6.8

// выводим информацию с использованием индексов

print(" Код ответа — \(myProgramStatus.0)")

print(" Текст ответа — \(myProgramStatus.1)")

print(" Связь с сервером — \(myProgramStatus.2)")

Консоль:

Код ответа — 200

Текст ответа — In Work

Связь с сервером — true

Доступ к элементам кортежа через имена

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

В листинге 6.9 показан пример определения имен элементов кортежа через литерал.

Листинг 6.9

let statusTuple = (statusCode: 200, statusText: "In Work", statusConnect: true)

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

В листинге 6.10 показано, как используются имена элементов совместно с индексами.

Листинг 6.10

// выводим информацию с использованием индексов

print(" Код ответа — \(statusTuple.statusCode)")

print(" Текст ответа — \(statusTuple.statusText)")

print(" Связь с сервером — \(statusTuple.2)")

Консоль:

Код ответа  — 200

Текст ответа  — In Work

Связь с сервером  — true

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

Как говорилось ранее, имена элементов могут быть заданы не только в литерале кортежа, но и при явном определении типа данных. В листинге 6.11 показан соответствующий пример.

Листинг 6.11

/* объявляем кортеж с

указанием имен элементов

в описании типа */

let anotherStatusTuple: (statusCode: Int, statusText: String, statusConnect: Bool) = (200, "In Work", true)

// выводим значение элемента

anotherStatusTuple.statusCode // 200

Редактирование кортежа

Для однотипных кортежей можно производить операцию инициализации значения одного кортежа в другой. Рассмотрим пример в листинге 6.12.

Листинг 6.12

var myFirstTuple: (Int, String) = (0, "0")

var mySecondTuple = (100, "Код")

// копируем значение одного кортежа в другой

myFirstTuple = mySecondTuple

myFirstTuple // (.0 100, .1 "Код")

Кортежи myFirstTuple и mySecondTuple имеют один и тот же тип данных, поэтому значение одного может быть инициализировано в другой. У первого тип задан явно, а у второго через инициализируемое значение.

Индексы и имена могут использоваться для изменения значений отдельных элементов кортежа (листинг 6.13).

Листинг 6.13

// объявляем кортеж

var someTuple = (200, true)

// изменяем значение отдельного элемента

someTuple.0 = 404

someTuple.1 = false

someTuple // (.0 404, .1 false)

Кортежи очень широко распространены в Swift. К примеру, с их помощью вы можете с легкостью вернуть из вашей функции не одно, а несколько значений (с функциями мы познакомимся несколько позже).

ПРИМЕЧАНИЕ Кортежи не позволяют создавать сложные структуры данных, их единственное назначение — сгруппировать некоторое множество разнотипных или однотипных параметров и передать в требуемое место. Для создания сложных структур необходимо использовать средства объектно-ориентированного программирования (ООП), а точнее, классы или структуры. С ними мы познакомимся в дальнейшем.

6.3. Сравнение кортежей

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

Листинг 6.14

(1, "alpha") < (2, "beta") // true

// истина, так как 1 меньше 2.

// вторая пара элементов не учитывается

(4, "beta") < (4, "gamma") // true

// истина, так как "beta" меньше "gamma".

(3.14, "pi") == (3.14, "pi") // true

//истина, так как все соответствующие элементы идентичны

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

Назад: Часть III. Контейнерные типы данных
Дальше: 7. Последовательности и коллекции