obo.dev

Кортежи (Tuple)

05 Dec 2022

Кортежи (Tuple)

В Swift кортежи используются для создания упорядоченных списков значений.

Что такое кортеж?

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

Синтаксис кортежа

В общей форме кортеж записывается так:

var tupleName: (DataType, ...) = (value1, ...)

Здесь:

  • (DataType, ...) - тип данных кортежа: в круглых скобках указаны типы данных элементов;
  • (value1, ...) - значения элементов кортежа: в круглых скобках указаны значения элементов.

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

Пример:

var tupleName = (value1, ...)

Пример кортежа:

let flight = ("LAX", 747)

В приведённом выше примере создаётся константа flight, которая инициализирована со значением в виде кортежа ("LAX", 747). В кортеже можно увидеть два значения, которые разделены запятой и которые заключены в круглые скобки.

Тип данного кортежа (String, Int) - определён из типа данных его элементов.

Так как кортеж - это упорядоченный список, то порядок элементов имеет значение. Кортеж по типу (Int, String) и кортеж по типу (String, Int) - это два разных кортежа по типу данных.

Приведенные ниже кортеж имеют разные типы:

("LAX", 747) // (String, Int)
(747, "LAX") // (Int, String)

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

Не следует путать кортежи с массивами, словарями или множествами. Кортежи не являются коллекциями, однако, они так же объединяют значения.

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

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

Операции с кортежами

Предположим, необходимо хранить информацию о названии и цене некоторого продукта. Можно создать кортеж с одним значением для хранения имени (String) и другим значением - для хранения цены (Float).

Создание кортежа

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

Например:

var product = ("MacBook", 1099.99)

Здесь product — это кортеж со строковым значением "Macbook" и целым числом 1099,99.

Получение значений элементов кортежа

Как и в массиве, каждый элемент кортежа имеет индекс, который представлен порядковыми номерами (0, 1, …), где первый элемент имеет индекс 0.

Для доступа к элементам кортежа используется номер индекса.

В отличие от массивов, где доступ по индексу осуществляется с помощью квадратных скобок ([]), в кортежах используется конструкция “обозначение через точку” (dot-notations) - как при получении доступа к элементам классов, структур: пишется имя кортежа, потом ставится точка . и за ней - необходимый индекс.

Например:

// access the first element
product.0

// access second element
product.1

Пример кортежа:

// create tuple with two elements
var product = ("MacBook", 1099.99)

// access tuple elements
print("Name:", product.0)
print("Price:", product.1)

// Output
// Name: MacBook
// Price: 1099.99

В приведенном выше примере был создан кортеж с именем product с двумя значениями: "MacBook" и 1099.99. Здесь использовался номер индекса: product.0 и product.1 для доступа к элементам кортежа.

Примечание. Поскольку первое значение кортежа — это строка, а второе — целое число, то тип кортежа — (String, Int).

Попытка доступа к элементу по несуществующему индексу приведёт к ошибке.

Например:

var company = ("Programiz", "Apple")

company.2 // Error: Value of tuple type '(String, String)' has no member '2'

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

Изменение элемента кортежа

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

Например:

// create tuple with two elements
var product = ("MacBook", 1099.99)

print("Original Tuple: ")

// access tuple elements
print("Name:", product.0)
print("Price:", product.1)

// Output
// Original Tuple:
// Name: MacBook
// Price: 1099.99

// modify second value
product.1 = 1299.99

print("\nTuple After Modification: ")

// access tuple elements
print("Name:", product.0)
print("Price:", product.1)

// Output
// Tuple After Modification:
// Name: MacBook
// Price: 1299.99

В приведенном выше примере был создан кортеж с именем product со значениями: MacBook и 1099,99. Обратите внимание на строку:

product.1 = 1299.99 

Здесь было изменено значение кортежа с индексом 1: с 1099.99 на 1299,99.

Примечание. Индекс кортежа всегда начинается с 0. Следовательно, первый элемент кортежа имеет индекс 0, а не 1.

Добавление/удаление элементов из кортежа

Нельзя добавлять или удалять элементы из кортежа в Swift.

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

Пример:

var company = ("Programiz", "Apple")

company.2 = "Google" // Error: Value of tuple type '(String, String)' has no member '2'

Нельзя удалить элемент, метода remove() нет для кортежа.

Пример:

var company = ("Programiz", "Apple")

company.remove("Apple") // Error: Value of tuple type '(String, String)' has no member 'remove'

Здесь создали кортеж со значениями: "Programiz" и "Apple". Теперь тип кортежа фиксируется на (String, String). Итак, когда пытаемся добавить и удалить элементы из кортежа, то получаем ошибки.

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

Можно сравнивать кортежи друг с другом, если их типы одинаковы и если сами типы соответствуют протоколу Equatable. Другими словами, если можно сравнить значения, то можно сравнить и сами кортежи:

let a = ("AMS", 737)
let b = ("LAX", 737)
 
print(a == b)

// Output
// false

print(a < b)

// Output
// true

let c = ("AMS", "A330-800")
c == b // Error: Cannot convert value of type '(String, Int)' to expected argument type '(String, String)'

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

Именованные кортежи

В Swift также можно указать имена для каждого элемента кортежа.

Например:

var company = (product: "Programiz App", version: 2.1)

Чтобы получить доступ к элементам именованного кортежа, также можно использовать эти имена вместо номеров индексов:

// access "Programiz App"
company.product 

Доступ по имени элемента осуществляется так же, как и по индексу: используя точку (.) после имени кортежа.

Пример именованного кортежа:

// create named tuple
var company = (product: "Programiz App", version: 2.1)

// access tuple element using name
print("Product:", company.product)
print("Version:", company.version)

// Output
// Product: Programiz App
// Version: 2.1

В приведенном выше примере были предоставлены имена: product и version для первого и второго элемента кортежа. Использовалось “обозначение через точку” (dot-notations) для доступа к элементам и подставлялись имена для доступа к соответствующим значениям кортежа.

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

Вложенный кортеж

В Swift можно создать кортеж, как элемент другого кортежа.

Например:

var alphabets = ("A", "B", "C", ("a", "b", "c"))

Здесь есть кортеж ("a", "b", "c") в качестве третьего элемента кортежа alphabets. Это называется вложенным кортежем.

Пример вложенного кортежа:

var alphabets = ("A", "B", "C", ("a", "b", "c"))

// access first element
print(alphabets.0)

// Output
// prints "A"

// access the third element
print(alphabets.3)

// Output
// ("a", "b", "c")

// access nested tuple
print(alphabets.3.0)

// Output
// prints "a"

В приведенном выше примере обратите внимание на строку:

print(alphabets.3)

// Output
// ("a", "b", "c")

Здесь получили доступ к третьему элементу кортежа alphabets. Поскольку третий элемент также является кортежем, использовалось “обозначение через точку” (dot-notations):

alphabets.3.0

для доступа к первому элементу вложенного кортежа.

Передача значений кортежа в переменные или константы

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

Пример:

let (airport, _, heading) = ("LAX", 747, 270)
print(airport)

// Output
// LAX

Вот что происходит в приведенном выше коде:

  • Есть кортеж ("LAX", 747, 270). Он не присваивается отдельной переменной или константе, но вместо этого, его значения раскладываются на отдельные элементы и присваиваются отдельным константам;
  • Назначем константе airport значение "LAX";
  • Подчеркивание _ - это значение, которое игнорируется.

Еще пример. Предположим, что используем функцию, которая возвращает нам код состояния HTTP. Такой код обычно состоит из числа, например “404”, и некоторого текста.

Пример:

func getStatusCode() -> (Int, String) {
    return (404, "Not Found")
}
 
let (code, text) = getStatusCode()
print("Возвращено значение \(code) и текст: '\(text)'")

// Output
// Возвращено значение 404 и текст: 'Not Found'

Функция getStatusCode() возвращает кортеж типа (Int, String), который содержит два значения.

Если необходимо получить доступ к отдельным значениям, то раскладываем кортеж с помощью (code, text). Далее, можно использовать отдельные константы code и text, чтобы вывести на консоль нужные значения.

Больше про переменные можно узнать в отдельной статье. См. ссылку внизу.

Словарь и массив внутри кортежа

В Swift можно использовать словарь для добавления элемента в кортеж.

Например:

var laptopLaunch = ("MacBook", 1299, ["Nepal": "10 PM", "England": "10 AM"])
print(type(of: laptopLaunch))

// Output
// (String, Int, Dictionary<String, String>)

print(laptopLaunch.2)

// Output
// ["Nepal": "10 PM", "England": "10 AM"]

laptopLaunch.2["USA"] = "11 AM"
print(laptopLaunch.2)

// Output
// ["Nepal": "10 PM", "England": "10 AM", "USA": "11 AM"]

В приведенном выше примере, третий элемент кортежа — это словарь:

["Nepal": "10 PM", "England": "10 AM"]

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

laptopLaunch.2["USA"] = "11 AM"

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

Таким же способом можно использовать массив.

Например:

var laptopLaunch = ("MacBook", 1299, ["10 PM", "10 AM"])
print(type(of: laptopLaunch))

// Output
// (String, Int, Array<String>)

print(laptopLaunch.2)

// Output
// ["10 PM", "10 AM"]

laptopLaunch.2.append("11 AM")
print(laptopLaunch.2)

// Output
// ["10 PM", "10 AM", "11 AM"]

Больше про массивы и словари можно узнать в отдельной статье. См. ссылку внизу.

Кортежи в программировании на Swift

Кортежи весьма полезны в разработке для iOS. На данном этапе были рассмотрены несколько типичных вариантов использования кортежей. Что еще можно с ними сделать?

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

Например, есть такой класс Flight:

class Flight {
    var airport:String
    var airplane:Int
}
 
let flight = Flight(airport: "AMS", airplane: 330)
print("Мы летим в \(flight.airport)...")

Приведенный выше код сначала определяет класс Flight, а затем инициализирует экземпляр данного класса flight. Можно сделать то же самое с кортежем, с гораздо меньшим количеством кода:

let flight = (airport: "AMS", airplane: 330)
print("Мы летим в \(flight.airport)...")

Также можно использовать псевдоним типа (Typealias):

typealias Flight = (airport: String, airplane: Int)
let flight: Flight = ("HND", 737)
print("Мы летим в \(flight.airport)..."

Здесь создаётся новый псевдоним типа Flight для типа данных кортежа (airport: String, airplane: Int). Далее, можно использовать тип данных Flight вместо типа данных кортежа (airport: String, airplane: Int). Константа flight будет с типом данных кортежа, что позволит получать доступ к элементам по индексу или именам элементов.

Кортежи можно использовать для доступа к индексам и значениям элементов массива с помощью функции enumerated():

let drivers = ["Magnussen", "Raikkonen", "Hamilton", "Verstappen"]
 
for (index, name) in drivers.enumerated() {
    print("\(name) индекс: \(index)")
}

// Output
// Magnussen индекс: 0
// Raikkonen индекс: 1
// Hamilton индекс: 2
// Verstappen индекс: 3

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


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

Также информацию по кортежам можно получить на странице официальной документации.

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