Jump to content

Алгебраический тип данных

(Перенаправлено из Алгебраических типов )

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

Двумя распространенными классами алгебраических типов являются типы продуктов (т. е. кортежи и записи ) и типы сумм (т. е. тегированные или непересекающиеся объединения , типы совместного произведения или типы вариантов ). [1]

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

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

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

Алгебраические типы данных были представлены в Hope , небольшом функциональном языке программирования, разработанном в 1970-х годах в Эдинбургском университете . [2]

Односвязный список

[ редактировать ]

Одним из наиболее распространенных примеров алгебраического типа данных является односвязный список . Тип списка — это тип суммы с двумя вариантами: Nil для пустого списка и Cons x xs для объединения нового элемента x со списком xs для создания нового списка. будет объявлен односвязный список Вот пример того, как в Haskell :

data List a = Nil | Cons a (List a)

или

data [] a = [] | a : [a]

Cons — это аббревиатура от cons truct. Многие языки имеют специальный синтаксис для списков, определенных таким образом. Например, Haskell и ML используют [] для Nil, : или :: для Consсоответственно и квадратные скобки для целых списков. Так Cons 1 (Cons 2 (Cons 3 Nil)) обычно записывается как 1:2:3:[] или [1,2,3] в Haskell или как 1::2::3::[] или [1,2,3] в МЛ.

Двоичное дерево

[ редактировать ]

В более сложном примере бинарные деревья могут быть реализованы в Haskell следующим образом:

data Tree = Empty
          | Leaf Int
          | Node Int Tree Tree

или

data BinaryTree a = BTNil
                  | BTNode a (BinaryTree a) (BinaryTree a)

Здесь, Empty представляет собой пустое дерево, Leaf представляет листовой узел, и Node организует данные по ветвям.

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

Конструктор данных, несколько похожий на функцию, применяется к аргументам соответствующего типа, создавая экземпляр типа данных, которому принадлежит конструктор типа. Например, конструктор данных Leaf логически является функцией Int -> Tree, что означает, что передача целого числа в качестве аргумента Leaf выдает значение типа Tree. Как Node принимает два аргумента типа Tree сам по себе тип данных является рекурсивным .

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

 depth :: Tree -> Int
 depth Empty = 0
 depth (Leaf n) = 1
 depth (Node n l r) = 1 + max (depth l) (depth r)

Таким образом, Tree отданный depth может быть построен с использованием любого из Empty, Leaf, или Node и должен быть сопоставлен с любым из них соответственно, чтобы иметь дело со всеми случаями. В случае Node, шаблон извлекает поддеревья l и r для дальнейшей обработки.

Абстрактный синтаксис

[ редактировать ]

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

data Expression = Number Int
                | Add Expression Expression
                | Minus Expression Expression
                | Mult Expression Expression
                | Divide Expression Expression

Элемент такого типа данных будет иметь такую ​​форму, как Mult (Add (Number 4) (Minus (Number 0) (Number 1))) (Number 2).

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

Сопоставление с образцом

[ редактировать ]

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

Например, рассматривая двоичный файл Tree показанном выше примере, конструктор не может нести никаких данных (например, Empty) или один фрагмент данных (например, Leaf имеет одно значение Int) или несколько фрагментов данных (например, Node имеет два Tree ценности).

Чтобы сделать что-то со значением this Tree алгебраический тип данных, он деконструируется с помощью процесса, называемого сопоставлением с образцом . Это предполагает сопоставление данных с рядом шаблонов . Пример функции depth шаблон, приведенный выше, сопоставляет свой аргумент с тремя шаблонами. При вызове функции она находит первый шаблон, соответствующий ее аргументу, выполняет привязку всех переменных, найденных в шаблоне, и оценивает выражение, соответствующее шаблону.

Каждый шаблон выше имеет форму, напоминающую структуру некоторого возможного значения этого типа данных. Первый шаблон просто соответствует значениям конструктора. Empty. Второй шаблон соответствует значениям конструктора Leaf. Шаблоны рекурсивны, поэтому данные, связанные с этим конструктором, сопоставляются с шаблоном «n». В этом случае идентификатор в нижнем регистре представляет собой шаблон, который соответствует любому значению, которое затем привязывается к переменной с этим именем — в данном случае к переменной « n” привязано к целочисленному значению, хранящемуся в типе данных, которое будет использоваться в выражении для оценки.

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

Node (Node (Leaf 4) x) (Node y (Node Empty z))

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

Приведенный выше пример функционально эквивалентен следующему псевдокоду :

 switch on (data.constructor)
   case Empty:
     return 0
   case Leaf:
     let n = data.field1
     return 1
   case Node:
     let l = data.field1
     let r = data.field2
     return 1 + max (depth l) (depth r)

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

Во-первых, это безопасность типов . В приведенном выше примере псевдокода от программиста требуется усердие, чтобы не получить доступ к поле2 когда конструктор является Leaf. Кроме того, тип поле1 отличается для Leaf и Node. Для Лифа это Int но для Node это так Дерево . В системе типов возникнут трудности с безопасным назначением статического типа для традиционных структур данных записи . Однако при сопоставлении с образцом таких проблем не возникает. Тип каждого извлеченного значения основан на типах, объявленных соответствующим конструктором. Количество значений, которые можно извлечь, известно на основе конструктора.

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

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

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

Например, тип данных Haskell:

 data List a = Nil | Cons a (List a)

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

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

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

В теории множеств эквивалентом типа суммы является непересекающееся объединение , набор, элементами которого являются пары, состоящие из тега (эквивалентного конструктору) и объекта типа, соответствующего тегу (эквивалентного аргументам конструктора). [3]

Языки программирования с алгебраическими типами данных

[ редактировать ]

Многие языки программирования включают алгебраические типы данных в качестве понятия первого класса, в том числе:

См. также

[ редактировать ]
  1. ^ Записи и варианты — раздел руководства OCaml 1.4. Архивировано 28 апреля 2020 г. на Wayback Machine.
  2. ^ Пол Худак; Джон Хьюз; Саймон Пейтон Джонс; Филип Вадлер. «История Haskell: лень с классами» . Материалы третьей конференции ACM SIGPLAN по истории языков программирования . В докладах участвовали Род Берстолл, Дэйв МакКуин и Дон Саннелла, посвященные Hope, языку, который представил алгебраические типы данных.
  3. ^ Эта статья основана на материалах, взятых из Algebraic+data+type в Бесплатном онлайн-словаре вычислительной техники до 1 ноября 2008 г. и включенных в соответствии с условиями «повторного лицензирования» GFDL версии 1.3 или более поздней.
  4. ^ Исчисление индуктивных конструкций и основные стандартные библиотеки: Datatypes и Logic.
  5. ^ «CppCon 2016: Бен Дин «Эффективное использование типов» » . Архивировано из оригинала 12 декабря 2021 г. – на сайте www.youtube.com.
  6. ^ «Модификатор запечатанного класса» . Дарт .
  7. ^ «Алгебраические типы данных в Haskell» . Серокелл .
  8. ^ «Экземпляр перечисления» . Haxe — Кроссплатформенный набор инструментов .
  9. ^ «JEP 395: Отчеты» . OpenJDK .
  10. ^ «JEP 409: Запечатанные классы» . OpenJDK .
  11. ^ «Запечатанные классы — язык программирования Kotlin» . Котлин .
  12. ^ «Reason · Reason позволяет писать простой, быстрый и качественный типобезопасный код, используя при этом экосистемы JavaScript и OCaml» . причинаml.github.io .
  13. ^ «Перечисления и сопоставление с образцом — язык программирования Rust» . doc.rust-lang.org . Проверено 31 августа 2021 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 75a9a1a065097c1b07cc45d5841588a3__1722207360
URL1:https://arc.ask3.ru/arc/aa/75/a3/75a9a1a065097c1b07cc45d5841588a3.html
Заголовок, (Title) документа по адресу, URL1:
Algebraic data type - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)