Кортежи (tuples) в Swift

Что это такое и как их можно использовать?
20 марта 2017F0a70d847660e5d6f039bc885e4d0c203ae64fe4max mas106615

Немного о том, зачем я создал эту статью

Смысл в том, чтобы сделать для себя свой собственный конспект по изучаемой теме. Так или иначе спустя некоторое время приходится возвращаться к изученным материалам, чтобы уточнить какие-то моменты. Ведь всё в памяти не удержишь. Пусть этот конспект будет подспорьем для краткого и быстрого повторения темы. Конечно, напрашивается вопрос: "Почему бы в нужный момент не посмотреть готовый текст в учебнике?" Отвечаю:

- В книгах изложение идёт от лица авторов. Часто это профессиональные программисты и материал подаётся с позиции полученного опыта и навыков. И не у всех из них есть навыки преподавателя. Например, многие обещают научить с нуля, но уже на 2-3 уроке начинают обсуждать вещи, которые новичкам абсолютно не понятны. Из-за этого многие вещи просто пропускаются. Например, что такое «консоль», что такое «Unicode», что значит «зарезервировать память» и др.

Кроме того, такой конспект является показателем самостоятельной проработкой темы. Чтобы её освоить, информацию стоит брать из нескольких учебников, плюс форумы, гугл и прочих материалов. В итоге, мы глубже прорабатываем тему и излагаем её так, как понимаем и запоминаем сами. А ещё в процессе обязательно находишь некоторую общую структуру изложения. И тогда каждая осваемая тема запоминается по этой общей схеме.

Общая схема изложения материала внутри глав учебника

По идее, каждая тема имеет подобную структуру:

  • Что это такое? Определение понятия. Словарный анализ слова.
  • Приведение к более высокому уровню, более общему понятию.
  • Отличие от тех понятий, которые на этом же уровне.
  • Определение через примеры, частные случаи данного понятия.
  • Синтаксис. Методы, свойства, особенности данной сущности (объекта, метода, типа данных и т.д.).
  • Как объявляется?
  • Какие методы, действия, операции могут использоваться.
  • Где применяется, насколько часто, насколько это важно (критично, актуально).

Примечания по ходу изложения

Чтобы не повторять часто два слова - константа и переменная - в дальнейшем будем использовать слово «переменная», подразумевая под ней и константу. Явно вспоминать про константу будем только тогда, когда работа с ней будет отличаться от использования переменной. Точно также слова «кортеж» и tuple будут употребляться как синонимы.

What is this? Определение понятия

Tuple - в переводе это кортеж. Кортеж - это когда по улице едут несколько машин. Например, свадебный кортеж. Или когда по улицам проезжает процессия автомобилей президента, своего или приезжего.

Tuple (кортеж) в программировании

Подведение под более общее понятие. Определение через подведение к более общему понятию.

Tuple - это ещё один тип данных. Т.е. более общее понятие - это тип данных, а Tuple - это один из возможных типов данных.

Отличие от других типов данных. Определение через отличие от других понятий того же уровня.

Отличие Tuple от других типов данных состоит в том, что это составной, сборный тип данных.

Т.е. если мы возьмём наши известные фундаментальные, примитивные типы данных, такие как Int, String, Double, то их ещё можно условно назвать "одиночными" типами. Это означает, что если у нас есть константа или переменная с типом данных, например, Int, то в ней могут храниться данные только одного типа Int. При этом каждая константа или переменная может хранить только одно значение. Tuple же может хранить одновременно несколько значений, которые в свою очередь могут быть нескольких типов. Например, в одном Tuple могут быть одновременно значение типа Int и значение типа String.

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

Примеры. Определение через понятия нижнего уровня.

Какие могут быть тюплы, как они выглядят? Рассмотрим на примерах.

Пример 1

let http404Error = (404, "Not Found")

В данном случае мы имеем константу по имени http404Error, которой присвоены сразу 2 значения, разные по типу данных. Одно из них число (404), второе - строка ("Not Found"). У первого значения тип данных Int, у второго - String. Это известное сообщение, когда браузер не находит запрошенную страницу. Так называемая "ошибка 404".

Пример 2

let infoStudent = ("Petr Sidorov", 2016, 9.5, true)

Условный пример - вступительная информация о студенте в базе данных института. В одной константе несколько значений (ФИО, год поступления, средний бал на вступительных и отметка о поступлении) разного типа - String, Int, Double, Bool.

Пример 3

var sales = (123, 32, 68, 15, 33.8, "Cheremushki", true)

Условный пример - продажи определённых товаров в одном из магазинов торговой сети. В одной переменной несколько значений разных типов данных: количество продаж нескольких товаров - Int, Int, Int, Int, один товар в дробном числе, например, вес в килограммах - Double, название магазина - String и отметка о закрытии записи в базе - Bool.

Синтаксис, свойства, методы кортежей / Tuples

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

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

Создание кортежей. Всего я насчитал 5 способов создания тюплов (кортежей).

1. Создание тюпла из готовых констант и переменных

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

var tupleSample = (peremennayaSmall, peremennayaMidle, konstantaLarge)

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

2. Создание тюпла с одновременным созданием констант и переменных с их начальными значениями

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

let number = 10

А вот как создаётся / объявляется tuple:

let adress = (10, "Street")

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

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

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

var adress2 : (Int, String)

А затем присвоить этому кортежу значения:

adress2 = (12, "Square")

Можно конечно сразу указать и то, и другое:

var adress3 : (Int, String) = (15, "Avenu")

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

4. Создание тюпла с одновременным созданием констант и переменных и наименованными элементами значений

А вот следующие два способа объявления Tuple немного специфичны. В частности, вот такой:

var adress5 = (numberHouse:21, nameDistrict:"Manhattan")

Что здесь появилось нового? Каждому значению Tuple присвоено имя. Зачем это нужно? Это бывает очень удобно, когда, например, приходит Tuple, а мы не знаем или забыли, что означают его значения - 21 и Manhattan. Особенно если значений в Tuple может быть не два и не три, а 5, 8, 32 и т.д. Мы помним, что их может быть сколько угодно.

В таких вот случаях можно использовать дополнительные названия для значений. На английском это звучит как «named elements», т.е. именованные элементы или элементы с названиями. Синтаксис у этих «named elements» такой: сначала пишется название по тем же правилам, что и названия переменных (с маленькой буквы и т.д.), затем без пробела двоеточие и затем тоже без пробела значение - см. пример выше.

5. Создание тюпла с одновременным созданием констант и переменных и составным наименованием тюпла

Наконец, ещё один способ создать (объявить) Tuple следующий:

var (houses, districts) = (numberHouse:21, nameDistrict:"Manhattan")

Обратите внимание на важную особенность - у созданного здесь Tuple вроде бы нет названия, т.е. после «var» сразу идёт скобка. На самом деле, как уже можно было догадаться, в данном случае используется составное название переменной. Оно, как и другие части Tuple, берётся в круглые скобки. Т.е. мы имеем название Tuple в скобках после слова «var». И это общее название состоит из двух названий - houses и districts.

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

Кажется, что здесь есть какой-то конфликт между составным названием Tuple и названиями элементов (значений). Ведь и там, и там есть названия. Кроме того, названия элементов иногда называются и как «внешние переменные». Что это такое, новичку пока трудно судить. Но в целом понятно, что оно перекликается с переменными. Отсюда можно сделать возможный вывод, что они могут конфликтовать между собой.

Но оказывается, что даже если упростить выражение (убрать названия элементов и оставить только составное название переменной), то значения в песочнице всё равно не появляются. Смотрите тот же скриншот на последней строке.

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

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

  1. создание Tuple из готовых констант и переменных;
  2. создание одновременно Tuple и констант или переменных с их начальными значениями;
  3. создание без значений, но с указанием типов данных для значений;
  4. создание со значениями и с названиями для них (named elements);
  5. создание Tuple с его составным названием.

Методы и особенности кортежей / tuples

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

Напоминаем себе, что процесс создания тюпла состоит в объединении нескольких значений в одну составную переменную. Отсюда следующий вопрос: как потом использовать такую составную переменную или константу? Есть два пути использования Tuple:

  1. Чаще всего для использования Tuple проделывается обратная операция. Он раскладывается на отдельные части и дальше уже эти части используются как обычные константы и переменные. Этот процесс или метод называется декомпозиция: decomposition (разложение, разборка).
  2. В некоторых случаях Tuple можно использовать и целиком, не разбирая его, или разбирая, но не полностью, а частично.

1. Разборка Tuple

Разобрать Tuple на отдельные элементы можно несколькими способами.

1.1 Разборка Tuple по готовым константам и переменным

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

1.2 Разборка Tuple по индексам значений

Возьмем следующий пример:

let travelRoute = ("Pacific Coast Highway", "SR 1", 883)

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

Если вы обратили внимание, то на скриншотах значения тюплов в песочнице выводятся, во-первых, в скобках, поскольку это Tuple. А во-вторых, перед каждым значением стоит цифра. Это и есть числовой индекс значения внутри Tuple. Смотрим скриншот:

Нумерация индексов начинается с 0. Чтобы взять из тюпла отдельное значение, можно указать название переменной и через точку добавить номер индекса. В итоге, мы можем работать с этими новыми переменными и константами по отдельности, как с обычными переменными и константами. Смотрим скриншот: 

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

1.3 Разборка тюпла по названиям элементов значений

Предположим, что у нас есть медицинская база и в ней создан Tuple с основными данными о пациенте: ФИО, возраст, рост, вес и т.д. При этом каждый элемент значения имеет своё название. Чтобы нам не путаться с нумерацией индексов, мы разбираем Tuple по этим названиям.

Принцип работы по разбору такого Tuple точно такой же, как и в предыдущем примере. Только вместо индексов используются названия элементов значений. Берём общее название переменной и через точку добавляем название элемента значения. В итоге получаем отдельные переменные, с которыми можно работать в обычном порядке. Смотрим скриншот:

Как видим, всё очень просто.

1.4 Разборка Tuple по названиям переменной

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

let salesAll = ("Ivanov", 31, 8000.55)

Теперь этот Tuple надо разобрать и раздать информацию в разные отделы. Можно, конечно, как и в предыдущих примерах, использовать общее имя константы и через точку указывать индексы. Или присвоить элементам значения свои названия.

Но можно сделать и ещё одним способом и в некоторых случаях он будет проще и понятнее. А именно - разложить Tuple на отдельные переменные, у каждой из которых будет понятное и отдельное имя. Тогда эти данные можно раздавать разным пользователям системы, чтобы они ими пользовались для своих расчётов: бухгалтерии, отделу труда и заработной платы, руководителю отдела продаж и т.д.

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

var (manager, days, amount) = salesAll

В итоге, мы имеем возможность использовать каждую переменную по отдельности и можем работать с ними в обычном режиме. Сморим скриншот:

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

Что нам не нужно, мы отмечаем нижним подчеркиванием. При этом важно точно соблюдать и количество названий переменных, которые должны соответствовать количеству названий элементов значений, и место, где находятся те или иные элементы. Если нам нужен только третий элемент значения, как показано на скриншоте, то и в составном названии Tuple мы оставляем только третье название. А два предыдущих экранируем с помощью нижнего подчеркивания.

2. Использование Tuple без разборки или с частичной разборкой

В процессе изучения темы я нашёл три таких способа. Основу для этих примеров подсказали видеоуроки Алексея Скутаренко.

2.1 Вывод группы значений на печать через Tuple

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

Замеры производятся каждый час и выводятся на печать для отчёта. А также передаются специалистам, которые с ними работают. В том числе и на телевидение для представления текущих прогнозов погоды. Таким образом, мы имеем следующие переменные:

  • date, time, location, temperatureAir, pressureAtmosphere, speedWind и др.

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

print(date, time, location, temperatureAir, pressureAtmosphere, speedWind)

Вместо этого можно один раз сформировать Tuple и потом выводить его на печать и передавать другим пользователям:

var summary = (date, time, location, temperatureAir, pressureAtmosphere, speedWind) 

print(summary)

Т.е. мы собрали Tuple по первому способу, а дальше уже используем его целиком.

2.2 Передача значений тюпла другому тюплу

В этом случае Tuple также используется без разборки, целиком. Например, есть такой тюпл:

let a = (x:1, y:2)

И есть вторая переменная, тоже Tuple:

var b = (x:2, y:3)

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

b = a

Чтобы убедиться, что значения присвоены другой переменной, т.е. что Tuple передан другому тюплу, можно посмотреть результат в песочнице или вывести значения на печать:

print(b)

В итоге, смотрим на скриншот:

Как видим в песочнице и консоли переменная b получила новые значения от Tuple a, и вместо прежних 2 и 3 теперь у неё 1 и 2.

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

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

2.3 Взаимодействие между тюплами

Что называется, обнаружены незадокументированные возможности! Вполне возможно, что об этом говорится где-то в хелпах по Swift, но в тех учебниках, которые я штудировал, об этом в соответствующих главах ничего не сказано. А именно о том, что тюплы, как и обычные константы и переменные, можно складывать, вычитать и т.д.

Ниже на скриншоте показаны примеры таких операций, которые я наэкспериментировал в XCode:

Обратите внимание, что такие операции можно делать не только с целыми тюплами, но и с их частями. А также некоторые операции можно производить не только над числовыми значениями, но и над текстовыми (строковыми). Например, можно сложить (свести в одну строку) два текстовых значения из разных тюплов.

Где и как используются Tuples / кортежи

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

Особенно это полезно при возвращении значений из функций. Дело в том, что по умолчанию функцию возвращает только одно значение. И если нужно вернуть сразу несколько, то программистам приходится дополнительно писать методы, использовать указатели и т.д. Что это такое, я ещё точно не знаю. Главное - пока понятен сам факт, что приходится делать дополнительную работу, причём достаточно трудоёмкую.

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

Резюме по Tuples / кортежам

Оказывается, при такой систематизации информации для запоминания остаётся не так уж и много. И главное, она практически понятна. К

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

Создаются тюплы 5-ю способами:

  • из готовых констант и переменных;
  • Tuple создаётся вместе с созданием констант и переменных сразу с их значениями;
  • Tuple создаётся вместе с созданием констант и переменных без значений, но с указанием типов данных значений;
  • могут также использоваться названия элементов значений - named elements;
  • могут использоваться наименования переменных или констант внутри составного названия тюпла.

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

Разбирать тюплы можно следующими способами:

  • по изначально готовым константам и переменным;
  • по индексам значений;
  • по названиям значений - named elements;
  • по названиям переменных или констант внутри составного названия тюпла (здесь же можно брать из Tuple не все значения, а некоторые; остальные экранируются с помощью нижнего подчеркивания - underscore).

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

Заключение

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

И конечно, важны систематизация и структурирование материала, которые удобны для обучающегося. Так оно лучше понимается и запоминается. 

Желаю всем успехов в обучении!