Pull to refresh

Comments 54

Ах perl какой же он древний что про него забыли.
А ведь все то вы написали в нем уже есть.
Я не писал про «интерпретируемые» языки (Perl, Python, Ruby, PHP и т.д.), потому что там грань между «компиляцией» и «выполнением» несколько мягче. Можно конечно написать и про них еще статью) Ну или может это кто нибудь сделает, кто лучше с этими языками знаком.
А откуда информация, что «кортеж — это структура времени компиляции»?
Сходу источник не смог нагуглить.
Это следует, скажем так, из практики программирования и понимания того как все устроено изнутри. Не уверен что это гуглится… ну вот теперь будет гуглиться)
В книгах и статьях по тому или иному языку программирования обычно дается практическая сторона: делайте вот так — и вы получите вот такой результат, скорее разместите программу в аппсторе и заработаете $$$. Рассуждения о том, что является структурами времени компиляции или времени выполнения, там излишни. Просто говорится, что в кортеж, также как в структуру (struct) например не добавить новые элементы во время выполнения, а в список (list) — можно.
В Haskell ни в список ни в кортеж не добавить элементов во время выполнения, но они от этого не становятся одним и тем же
То, что в кортеж нельзя добавить новые элементы, не делает его структурой времени компиляции, а лишь неизменяемой структурой данных. Есть много языков, в которых ни один экземпляр любого типа нельзя изменить во время выполнения. Но это не повод называть тот же list структурой времени компиляции.
Допустим, у нас есть кортеж {x, y}, если его конкретное значение может быть всегда вычислено на этапе компиляции, то да, это структура времени компиляции. Но если какой-то внешний фактор, например пользовательский ввод, может повлиять на значение x или y, то это уже совсем не структура времени компиляции.
По мне, кортеж — более глубокое понятие, чисто математическое. Например, в реляционной алгебре есть понятие кортежа, и оно по некоторым параметрам сходно — например, строка из БД — это кортеж — совокупность полей. Хотя здесь речь вообще о компиляции не идет, это чисто абстрактное понятие.
Существование математического понятия с этим названием никак не мешает существованию программистского.
Собственно из этого утверждения «кортеж — это структура времени компиляции» и исходят все ваши рассуждения в статье.
Это утверждение изначально неверное, даже в той же вики вы можете прочитать «Ordered pairs are also called 2-tuples», а уж пара никак не структура времени компиляции.
Списки гомогенны, кортежи гетерогенны. Отсюда вся пляска с фиксацией размера кортежа и прочие вытекающие.
И да, мы можем добавлять элементы в кортеж, получая новый экземпляр с новым типом (смотреть гетерогенные списки)
Т.е. получая новый кортеж.
Конечно, мне стоило отметить что речь идет в первую очередь о классических компилируемых языках. Чисто функциональные языки я не рассматривал, в них вообще всё по другому, и в первую очередь все данные иммутабельны.
Да в любом случае, вы рассматриваете кортежи в отрыве от типов, что в корне не верно в языке со статической типизацией, будь то императивная сишка или функциональный хк.
UFO just landed and posted this here
UFO just landed and posted this here
Он все правильно сказал. Списки гомогенны, т.к. List<Integer> или List<String>. А вот кортежи гетерогенны, т.к. (Integer, String, Double). Вся суть в типах элементов.
Вообще-то автор не Си обсуждает, а вообще…
Берём так любимый нами AppleScript и его списки:
set exampleList to {213.1, 6, «Julia, the actress», 8.5}
Обмен значений двух переменных:
set {a, b} to {b, a}
Это всегда был «списко-ориентированный» язык, и никакими кортежами тут мне голову не дурили.

Берём другой язык, списки значений в 1С:
Список.Добавить(1980, «Год рождения Васи»);
Ну так и в java можно сделать List<Object>. Вопрос лишь в том, как этим потом пользоваться.

Ну и если на то пошло, то, имхо, обсуждать различные типы на примере языков со слабой динамической типизацией — это, как минимум, странно.
Кортеж, на самом деле, может быть достаточно неудобен, потому что элементы в нём имеют неосмысленные имена вида «первый», «второй», «тот», «этот» и «ещё один». Чтобы было больше смысла, лучше пользоваться структурами с именованными полями. А если имеется в виду порядковая нумерация элементов — для этого есть массивы.
Несколько позабавило то, что рассмотрение кортежей идет в отрыве от тех языков, в которых они действительно используются, скажем так, широко… Haskell, Erlang тот же и т.п.

Ну и да… повеселило даже не про «времени компиляции», а «кортеж — это не тип данных» :-)
Смотря что понимать под типом данных.
Я бы сказал, что кортежи свободно приводятся к типам данных, но все-же не являются полноценными типами. Например
{1, 2, 3}

— это что? Массив из трех целых? Структура из трех целых? Список (list)? Множество (set)? Что-то еще?
— это что? Массив из трех целых? Структура из трех целых? Список (list)? Множество (set)? Что-то еще?

:-) Это, например, _кортеж_ {int, int, int}.

Ну и — на в скидку — к какому _типу данных_ «свободно приводится» какой-нибудь {string, int, boolean}?
К соответствующей структуре, разумеется.
А точнее — к соответствующим по типам полей структурам. Самих структур может быть бесконечно много, и все они по умолчанию не будут совместимы между собой (номинативная типизация).
Вот именно. А _кортеж_ {string, int, boolean} — от такой один единственный.

И тогда, нам остается выяснить, что именно вы подразумевали, говоря что «кортежи свободно приводятся к типам данных»?

Просто, даже ваше утверждение «к соответствующим по типам полей структурам» — оно, мягко говоря, не совсем корректное же. Каким образом вы на уровне _структур_ собираетесь различать {string, int, boolean} и {boolean, int, string}, например?
Я имею в виду примерно вот это:
struct Foo {
  string str;
  int i;
  boolean b;
}
struct Bar {
  string name;
  int k;
  boolean c;
}

Foo f;
Bar b;
f = b; // error
b = f; // error
f = {"hello", 10, true}; // ok
b = {"hello", 10, true}; // ok
{string, int, boolean} t1 = {"hello", 10, true}; // ok
{string, int, boolean} t2 = f; // ok
{string, int, boolean} t3 = b; // ok
Мне кажется, что пример — надуманный, потому что в первых двух структурах у вас именнованные поля (причём разными именами), а в коде — безымянные.
Пример вполне реальный. В структурах именованные поля, и дальше показано что даже если две структуры «структурно идентичны», они все равно несовместимы — то есть объект типа Foo нельзя присвоить объекту типа Bar, и наоборот. То есть первую часть (до кортежей) вы можете проверить практически на любом языке программирования.

Дальше показано, что поведение кортежей отличается от поведения структур — если два кортежа структурно идентичны, то их можно свободно присваивать между собой, и даже между объектами кортежа и структуры. Ближайший аналог подобного синтаксиса — Swift, возможно что-то сделают в C++17.
Причём здесь структуры? Объявите сначала тип, потом создайте две структуры этого типа и присваивайте наздоровье. Структуры так и останутся «структурно идентичны». То что в языках есть проблемы вывода типов, это не проблемы структур. Даже более того, вы выносите какую-то непонятную сущность и гвоорите — вот она, да ничего подобного, просто компилятор приводит тип справа к типу слева, с таким же успехом можно причмокивая обсасыватть такой пример:
var
a: real;
begin
a := 10;
end;

10 это, байт, а он, глядите-ка, присваивается дробным числам. От шайтан, что делается, да это ж кортеж одночленный. Тьфу!..
Объявите сначала тип, потом создайте две структуры этого типа и присваивайте наздоровье.

Так задача поставлена уже. Условия иные: у вас есть два типа, они одинаковы структурно, но они — разные сущности. И правил приведения типов для структур — нет. Можно считать, что кортеж — это структура с ограничениями, для которой определена операция приведения, в случае, если все поля — тех же типов и следуют в том же порядке.
А я вижу всего лишь данные, как в комментарии выше, где написано число 10 и не вижу никаких отличий поведения от приведённого мной примера.
var
a: real;
b: byte;
begin
a := 10;
b := 10;
end;

Ну давайте теперь поговорим о том, что 10 это магическая сущность, которая может являться и целым и действительным одновременно и она вне типов.
Расширяем пример:
var
a: record
i1: real;
i2: real;
end;
b: record
i1: byte;
i2: byte;
end;
begin
a := (i1:10; i2:10);
b := (i1:10; i2:10);
end;

Можно долго с умным видом рассуждать о вездесущности и приводимости (i1:10; i2:10), но де факто это две совершенно самостоятельные строки без каких-либо взаимосвязей, так же и у вас, то что типы там совпали — это совершенно фиолетово, это не одна сущность «кортеж», это два разных набора данных, по случаю совпавших и приводятся они в каждом конкретном случае именно потмоу, что это данные и их тип определён в левой части, а не в самих данных.
Ещё раз повторяю не стоит плодить непонятные сущности там, где их нет.
А кстати, что такое «10»? Какого типа? byte, short, word, int, uint, long, float, double?
Иногда у чисел бывают модификаторы, например можно представить что «10i32» — это int32. А без модификаторов? Брать «тип по умолчанию» («int»), как в Си? А правильно ли это?
В действительности правильный подход к литеральным константам такой — они сами по себе на этапе построения синтаксического дерева не полностью типизированы. Типизация происходит позднее, когда выясняется, в каком выражении с какими типами операндов эта константа используется.

Хотя эти вопросы больше касаются дизайна компиляторов, но это не делает их мене интересными. И это открывает дополнительные возможности — такие как архитектурно правильная интеграция в язык программирования «длинной арифметики».
Даже не знаю как до вас достучаться. Не имеет никакого значения какого типа число 10, это была иллюстрация вот к этому примеру:
f = {"hello", 10, true}; // ok
b = {"hello", 10, true}; // ok

Вы обзываете {«hello», 10, true} кортежем, а раз там и там кортеж, то это по вашему одно и то же.
Я же вам наглядно уже не первый пост пытаюсь показать, что {«hello», 10, true} <> {«hello», 10, true}, это совершенно разные данные, то что они выглядят одинаково с точки зрения компилятора, да и логики языка, ни о чём не говорит. Поэтому не имеет значения насколько равны у вас исходные структуры, им присваиваются разные данные.
На этом предлагаю дисскуссию завершить, если вы и тут меня не поняли, то я не вижу смысла далее разъяснять.
Даже не знаю как до вас достучаться. Не имеет никакого значения какого типа число 10, это была иллюстрация вот к этому примеру:

Ключевое в примере выше — вот это:

Foo f;
Bar b;
f = b; // error
b = f; // error

Если вы этого не понимате — печально.

Я же вам наглядно уже не первый пост пытаюсь показать, что {«hello», 10, true} <> {«hello», 10, true}, это совершенно разные данные,

А {string. int, boolean} и {string. int, boolean} — один и тот же тип.
В отличие от
struct Foo {
  string str;
  int i;
  boolean b;
}
struct Bar {
  string name;
  int k;
  boolean c;
}
это не одна сущность «кортеж», это два разных набора данных, по случаю совпавших и приводятся они в каждом конкретном случае именно потмоу, что это данные и их тип определён в левой части, а не в самих данных.

Нет, как раз новая сущность «кортеж» определяет то, что это одинаковые наборы данных. Правила приведения применяются в первую очередь для кортежа, а не его элементов. В итоге вы можете скопом передать, привести набор данных.
Кстати, из смешного. Про кортеж одночленный. Создателям Хаскеля тоже было смешно.
Как потом оказалось, это всё же удобная конструкция для определённых случаев.
И даже добавили внешнюю библиотеку с одночленным кортежем — пакет One Tuple
Да что там хаскел. Питон.
Поскольку синтаксис (x,y,z.....) в случае одночленного кортежа превращается в чисто декоративные скобки (x), — специально для этого придумали хак — (x,)
Спасибо… я, кажется, понял ваш ход мыслей.

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

А речь, таки, о _типах_. А вот в терминах типов, как раз можно смело утверждать что _любая_ структура, «свободно сводится» к соответствующему кортежу. Причем, хочешь — с сохранением «плюшек» номинативной типизации, хочешь — без. Но, выше вы упорно пишете что:

Можно считать, что кортеж — это структура с ограничениями, для которой определена операция приведения, в случае, если все поля — тех же типов и следуют в том же порядке.

В принципе, «можно считать» как угодно. Ктож нам запретит :-) Вопрос только: какой в этом смысл?! С тем же успехом я могу утверждать что это структура — это кортеж специального вида. И я даже знаю язык в котором это действительно так :-)
Я вас тоже понял. Конечно, кортеж это тип с определенной точки зрения. И для конечного программиста удобнее рассматривать кортеж именно как тип, обладающий какими-то особенностями — по сравнению например со структурами, которые обладают другими особенностями.
Но я выбрал несколько другую точку зрения, более близкую к внутреннему устройству компилятора, и с этой точки зрения кортеж — скорее "полуфабрикат", заготовка для "полноценного" типа.
Так например, список аргументов функции — это тип? Обычно нет (сама сигнатура функции — тип, но не отдельно список аргументов). Но при этом вполне можно сказать что список аргументов функции — это кортеж. Ну или рассматривать список аргументов функции как самостоятельный тип, что в общем одно и то же.
Но я выбрал несколько другую точку зрения, более близкую к внутреннему устройству компилятора, и с этой точки зрения кортеж — скорее «полуфабрикат», заготовка для «полноценного» типа.

Ну тогда можно как-то по подробнее про "внутреннее устройство компилятора"? И как быть в случаях когда этого самого "компилятора" вообще нет?! Следует ли это понимать так, что в этом случае типы отсутствую как таковые?

Вообще, ваше настойчевое противопоставление "типов компилятора", "типов определенной т.з" и "типов вообще" веселит меня гораздо больше, чем ваше упорство в попытке убедить общественность сначала в том, что "кортеж — не тип", а теперь, надо понимать, в том, что "кортеж — не настоящий тип". Я наверное таки возьму на себя смелость и поинтересуюсь у вас: "а что такое тип?". :-)

Так например, список аргументов функции — это тип? Обычно нет (сама сигнатура функции — тип, но не отдельно список аргументов). Но при этом вполне можно сказать что список аргументов функции — это кортеж.

Что значит "обычно нет"?! Это сильно зависит от определения функционального типа в данной конкретной системе типов. Если в СТ нет функционального типа, то у вас и "сама сигнатура функции" — не тип. Если есть, то все зависит от его определения. И это вполне может быть и не кортеж вообще. "Ф-ции одного аргумента достаточно для всего" (с) :-)
Думаю лучше будет если я попробую подробнее разъяснить свою точку зрения в следующей части.
Здесь да, признаюсь это моя ошибка — я сделал утверждение, которое является скорее моей оригинальной идеей, а не чем-то общепринятым, да еще и не объяснил что это за идея и почему я так считаю. Обязательно это учту.
В принципе, «можно считать» как угодно. Ктож нам запретит :-) Вопрос только: какой в этом смысл?! С тем же успехом я могу утверждать что это структура — это кортеж специального вида. И я даже знаю язык в котором это действительно так :-)

Это было мое утверждение. Смысл очень простой: есть важное свойство — приводимость, которое позволяет считать, что у нас есть отдельный новый тип — кортеж. Под "можно считать" я имел в виду ближайшую аналогичную конструкцию, и указал это отличие, которое позволяет считать к. отдельным типом.
UFO just landed and posted this here
   Point3D  pt{10,20,30}; // новый синтаксис инициализации
Point3D foo(Point3D a)
{
    return {1, 2, 3}; // возвращаем "кортеж"
}
foo( {3,2,1} ); // передаем "кортеж" 


Вот за это отдельное спасибо!
Вообще кортежи ИЗОМОРФНЫ(равнозначны) структурам.
Полностью.
Отличия следующие
1) Тип структуры вначале декларируется, потом используется. В то время как тип кортежа вычисляется на лету.
2) Кортеж — это структура с безыменным доступом до полей. Или же структура — это кортеж с именным доступом до полей.
3) Технологически построение кортежей и структур в языке может отличаться

Вообще странно, что вы не упомянули примером ни один функциональный язык, ибо кортежи/туплы в основном используются там. И оттуда перекочевали в Go и Rust
В Swift поля кортежа могут иметь имена.
И кстати, там интересные правила присваивания кортежей с именованными полями, о чем я не рассказал…
Кортеж с безымянными полями можно присвоить любому с именованными, и наоборот.
А если оба кортежа с именованными полями, то присваивание возможно только при совпадении имен полей.

Из того что я еще не рассмортел — Scala, а тоже стоило бы:)
Из всей статьи я так и не понял, чем отличаются списки от кортежей.
Более того, автор говорит о языках программирования и его несёт в структуры данных SQL, при этом в процедурных языках данные и код это совершенно разные вещи. Либо давайте конкретно обсуждать языки ориентированные на данные типа HTML (не вижу смысла в контексте кортежей), либо уже плавно съезжайте на объектные которые хоть и имеют парадигмы, но де факто обращаются с данными так же как процедурные языки. К чему компиляция вообще не понятно, я уже приводил пример для расспространённых интерпретаторов языков, таких как AppleScript и 1С . В частности меня интересует объектно ориентированный язык AppleScript, который так же является списко-ориентированным, вот конструкция где на лету создаётся список из двух переменных и обмениваются значения этих переменных:
set {a, b} to {b, a}

Ну а вот как создаётся именнованный список:
set exampleList to {213.1, 6, «Julia, the actress», 8.5}


Я понимаю что такое список, но я так и не понял, что такое кортеж, кроме того, что он везде и — нигде.
Можно сказать, что кортежи это «почти что структуры» (struct).
Чем отличаются списки от структур, я надеюсь что понятно:)
Более интересный вопрос — чем отличаются кортежи от структур, там отличия более тонкие (хотя статья не посвящена именно этим отличиям).
На сколько я понимаю, единственное принципиальное отличие структуры от кортежа в том, что она умеет рекурсию.
Еще структуры как правило номинативно типизированы; то есть объявленная структура создает тип, по умолчанию несовместимый с другими типами, даже если структурно эти типы идентичны.
habrahabr.ru/post/276871/#comment_8769577
Да, и еще поля структуры именованы. Но, имхо, это все уже вторично.
Боюсь вас расстроить, но структура, в классическом понимании, не является рекурсивным типом.
Sign up to leave a comment.

Articles