Что такое кортеж 1с

Обновлено: 06.07.2024

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

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

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

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

Кортеж - это потомок последовательности. Следовательно, возможен доступ к элементу кортежа по его индексу. Доступ к подпоследовательности элементов кортежа задается соответствующим срезом. Если в кортеже n элементов, то индекс элемента должен находиться в пределах от 0 до n - 1 . Это же верно для начального индекса среза. Конечный индекс среза может выходить за допустимые пределы, - такой срез задает подпоследовательность, заканчивающуюся последним элементом кортежа.

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

Основные свойства кортежей:

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

Кортежи являются типом значений.

Кортежи поддерживают операторы = и !=

Значения кортежей являются общедоступными полями.

Обращение к значениям кортежа происходит через поля с названиями типа Item[номер], где номер - порядковый номер значения в кортеже. Рассмотрим на примере программы:

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

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

Для определения кортежей можно применять переменные, с которыми в дальнейшем можно производить операции.

Применение кортежей

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

В следующем примере кортеж передается в метод в качестве параметра.

Присваивание кортежей

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

Деконструкция кортежей

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

Проверка кортежей на равенство

Имена полей кортежей при сравнении не учитываются.

Условия возможности сравнения кортежей:

Оба кортежа содержат одинаковое количество элементов.

Элементы должны соответствовать возможности сравнения.

Кортежи в качестве параметров вывода

При выполнении рефакторинга метода, имеющего параметры out они могут иметь тип кортежа.

Различия между типами кортежей System.ValueTuple и SystemTuple

Основные различия заключаются в следующем:

Типы ValueTuple являются типами значений - типы Tuple - ссылочные типы.

Типы ValueTuple являются изменяемыми типами - типы Tuple - неизменяемые.

Значениями типов ValueTuple являются поля. Значениями типов Tuple являются свойства.

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

Первая важная вещь, о которой на Википедии не упомянули: кортеж — это структура времени компиляции. Иными словами, это некая сущность, объединяющая некоторые объекты на этапе компиляции. И это очень важно. Кортежи неявно используются во всех языках программирования, даже в Си и Ассемблере. Давайте поищем их в том же Си, С++, в любом компилируемом языке.
Так, список аргументов функции — это кортеж;
Список инициализации структуры или массива — это тоже кортеж;
Список аргументов шаблона или макроса — тоже кортеж
Описание структуры, и даже обычный блок кода — это тоже кортеж; только элементами его являются не объекты, а синтаксические конструкции.

Кортежей в программе гораздо больше чем кажется на первый взгляд. Но они все неявные; так или иначе они являются жестко прикрученными к каким-то синтаксическим конструкциям. Явное использование кортежей в старых языках было не предусмотрено. В более современных языках некоторые возможности явного использования стали появляться — но далеко не все. Здесь мы будем рассматривать в основном кортежи значений — или переменных, или констант. Возможно, в следующих частях я рассмотрю и кортежи произвольных синтаксических элементов.

Начнем с самого очевидного — возврата нескольких значений из функции. Еще со школьных времен меня удивляла такая несправедливость: почему функция может принимать сколько угодно значений, а возвращать только одно? В самом деле, почему y=x*x — нормальная парабола, а y = sqrt(x) — какая-то обрезанная наполовину фигня? Разве это не нарушение математической гармонии? В программировании конечно можно возвратить структурный объект, но суть остается та же: возвращается один объект, а не несколько.

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

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


Такая пакетная передача сама по себе крайне интересна. С одной стороны, она кажется весьма элегантной; но с другой — она слишком «неявная», неуниверсальная. Например, если попытаться добавить в bar третий аргумент, и попытаться совместить «пакетную» передачу и обычную

то ничего не получится — ошибка компиляции.

Т.е. работает принцип «все или ничего»: можно или игнорировать все возвращаемые значения, или использовать — но также все.

В Rust имеются схожие возможности. Точно также функции могут возвращать несколько значений; также можно инициализировать ими новые значения. При этом множественное присваивание как таковое отсутствует, возможна только инициализация. Аналогично можно использовать символ "_" для неиспользуемых значений. Аналогично можно игнорировать возвращаемые значения полностью, или получать их также все полностью. Также кортежи можно сравнивать:

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

В языке Swift возможности в целом аналогичны. Из интересного — обращение к элементам кортежа по константному индексу через точку; возможность назначать элементам кортежа имена и обращаться к элементам через них.

Такие кортежи уже близки к структурам, но все-же структурами не являются. И здесь я бы хотел отойти от примеров и перейти к своим собственным мыслям. Разница между кортежами и структурами в том, что кортеж — это не тип данных, это нечто более низкоуровневое; можно сказать — что кортеж — это просто (возможно именованная) группа (возможно именованных) объектов времени компиляции. В этом месте вспомним языки C/С++. Простейшие конструкции инициализации массива и структуры выглядят так:

Обратите внимание, списки инициализации в данном случае вообще идентичны. И тем ни менее, они инициализируют совершенно разные объекты данных. Такое поведение в общем нетипично для типа данных. Но зато это близко к другой интересной фиче, которая иногда (но редко) встречается в языках программирования — структурной типизации. Конструкция в фигурных скобках — типичный кортеж. Кстати, в Си есть именованная инициализация полей структуры (идея кстати весьма похожа на Swift), которую пока так и не протащили в C++17:


В С++ пошли немного в другом направлении: ввели понятие " унифицированный синтаксис инициализации и списки инициализации". Синтаксически это те же кортежи, которые можно использовать для инициализации объектов; в дополнение к старым возможностям, унифицированный синтаксис инициализации позволяет передавать объекты в функции и возвращать из функций в виде кортежей.


Другая интересная возможность — списки инициализации.Они используются для начальной инициализации динамических структур данных — таких как вектора и списки. Списки инициализации в С++ должны быть однородными, то есть все элементы списка должны быть одного типа. Технически такие списки формируют константные массивы в памяти, для доступа к которым применяются итераторы std::initializer_list. Можно сказать, что шаблонный тип std::initializer_list — специальный определенный на уровне компилятора интерфейс к однородным кортежам (а фактически к константным массивам). Разумеется, списки инициализации можно использовать не только в конструкторах, но и как аргументы любых функций и методов. Думаю, если бы в С++ изначально был бы некий шаблонный тип данных, соответствующий литеральному массиву и содержащий информацию о длине этого массива, он бы вполне подошел на роль std::initializer_list.

Также в стандартной библиотеке С++ (и в Boost) существуют кортежи, реализованные с помощью шаблонов. Поскольку такая реализация не является частью языка, синтаксис получился слегка громоздким и неуниверсальным. Так, тип кортежа приходится объявлять явно с указанием типов всех полей; для конструирования объектов применяется функция std::make_tuple; для создания кортежа «на лету» (из существующих переменных) применяется другой шаблон — tie, а получение доступа к элементам осуществляется с помощью специального шаблонного метода, который требует константного индекса.

В примере применяется распаковка со специальным значением std::ignore. Это в точности соответствует символу подчеркивания "_", применяемому для тех же целей при групповых возвратах из функций в Go и Rust.

В языке D есть достаточно богатая поддержка кортежей. С помощью конструкции tuple можно сформировать кортеж, и — в том числе — осуществить множественный возврат из функции. Для доступа к элементам кортежа используется индексация константными индексами. Также кортеж можно сконструировать с помощью шаблона Tuple, что позволяет создать кортеж с именованными полями.


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

В D существует еще множество возможностей, связанных с кортежами — Compile-time foreach для обхода кортежей на этапе компиляции, шаблон AliasSeq, оператор tupleof… в общем все это требует отдельной большой статьи.

А напоследок рассмотрим реализацию кортежей в малоизвестном расширении языка Си — CForAll или C∀ (забавно, но на момент написания статьи я не смог нагуглить сайт языка — весьма вероятно что он давно закрылся и упоминаний просто не осталось; вот поэтому я регулярно сканирую сеть в поисках новых языков программирования и скачиваю все до чего смогу дотянуться).

Кортежи в C∀можно объявить на уровне языка, заключив список объектов в квадратные скобки. Тип кортежа создается аналогично — в квадратные скобки заключается список типов. Объекты и типы кортежей можно объявлять явно. Кортежи можно передавать в функции, где они разворачиваются в списки аргументов (в отличие от Go, где такое возможно только при точном совпадении кортежа и списка аргументов функции).

Еще одна интересная тема — вложенные кортежи и правила их раскрытия. В С/С++ также применяется вложенность — при инициализации массивов структур, элементы которых также являются массивами и структурами. В C∀ существуют правила, называемые «tuple coercions», в частности — раскрытие кортежей с внутренней структурой (flattering) и наоборот, структурирование (structuring), когда «плоский» кортеж подстраивается под сложную внутреннюю структуру (хотя эта возможность весьма спорная, обсуждение будет в следующей части). И все это относится лишь к присваиванию, никаких упоминаний использования этих возможностей с другими операциями нет.

В C∀ предусмотрено как групповое, так и множественное присваивание.

и даже использование кортежей для доступа к полям структур

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

Относительно недавно (в мае) Робин Рикард и Рик Баттон сделали предложение «Record & Tuple», которое добавляет два новых примитивных объектов в JavaScript:

  • кортежи (tuples) — неизменяемая и сравниваемая по значению версия массивов
  • записи (records) — неизменяемая и сравниваемая по значению версия простых объектов

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

А в TypeScript 4.0 добавляют еще и labels to tuple elements.

Но если вам нужны кортежи уже сейчас, как структуры данных, то их можно, в принципе эмулировать. Еще два года назад я писал подробно про кортежи в JS и TypeScript.

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

Коротко напомню, что кортежи несут еще один смысл:

Кортеж (tuple) — упорядоченный набор фиксированной длины.

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

На самом деле вы уже используете кортежи в JavaScript неявно и даже не подозреваете об этом. Кортежи неявно используются во всех языках программирования, даже в Си и Ассемблере.

Список аргументов функции или список инициализации массива является неявным кортежем

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

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

Явно реализуем кортежи в JS/ES2020

Так как в JS нет синтаксической конструкции для объявления кортежа, мы создадим функцию tuple (прям как в Python):

Мы получили неизменяемый список фиксированной длины.

Так же можно реализовать разные частные случаи кортежей, например - пара.

Частный случай кортежа — пара

Как понятно из названия пара может содержать только 2 значения, в то время как кортеж — любое количество. Реализация пары на JavaScript:

Кортежи в TypeScript

TypeScript уже давно позволяет типизировать структуры данных и дает нам возможность описать кортеж, содержащий разные типы. Допустим мы хотим кортеж, который содержит сразу два типа:

Так же кортеж можно описать через интерфейс, расширив базовый тип Array:

Таким образом можно создавать списки с фиксированными типами. Но если вспомнить что такое кортеж — это упорядоченный набор фиксированной длины. А у нас получились объекты, в которые можно добавить новые элементы.

Упорядоченный набор фиксированной длины

Мы можем указать длину нашего кортежа и TS будет проверять ее:

Как видите, TS говорит нам, что мы не можем объявить кортеж с тремя элементами, так как мы заявили всего 2. Но, как помните из предыдущих упоминаний, все это артефакты. Поэтому для рантайма нам нужно добавить заморозку объекта.

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

Описание кортежа таким способом дает следующие преимущества:

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

Кортежи в TypeScript 4.0

Собственно что предлагается в новой версии TS в области кортежей - это добавление нового синтаксиса:

Читайте также: