Что такое пользовательский тип данных
Лекция 6. Пользовательские типы данных¶
Простые типы¶
Классификация¶
Стандартные и нестандартные типы¶
Использование только стандартных типов в программе уменьшает её возможности.
Стандартные типы не обладают информативностью.
Нестандартные типы данных получаются с помощью специальных механизмов.
Описания нестандартных типов¶
Описания нестандартных типов обычно размещают:
Псевдонимы¶
Упрощение сложных объявлений¶
Объявить массив из N указателей на функции, возвращающих указатели на функции, возвращающие указатель на char.
Можно объявить этот массив так:
А можно воспользоваться typedef:
Перечисления¶
Основой для внутреннего представления перечислимого типа является тип int.
Слабая типизация С позволяет смешивать в программе числовые константы и перечислимые типы, а также использовать арифметические операции (++).
В С++ (с сильной типизацией) данный пример вызовет ошибку и будет требовать перегрузки арифметического оператора ++ для перечислимого типа.
Как связать перечисление со строкой¶
Составные типы¶
Структуры¶
Записи¶
Структура (запись) представляет собой набор данных, хранящихся в памяти в смежных адресах, но не обязательно принадлежащих одному типу. Это позволяет рассматривать саму структуру как универсальный тип для представления внутреннего устройства множества объектов.
Изображение структуры (записи):
Фундаментальные структуры данных¶
Замечательно то, что массивы и записи можно объединять, создавая:
На основе массивов и записей строят:
Определение структуры¶
Сначала мы описываем новый структурный тип:
Pascal: Занятие № 9. Типы данных пользователя Паскаль Type
Создание типов данных пользователя в Pascal
Типы данных в Pascal делятся на простые и сложные.
К простым типам относятся стандартные, перечисляемые и ограниченные.
К сложным типам – массивы, множества, записи, файлы. Элементами сложных типов могут быть простые и сложные типы. Мы познакомимся со сложными типами данных позже.
Одним из наиболее распространенных типов является порядковый стандартный тип.
Порядковый стандартный тип обозначает конечное линейное множество значений. К нему обычно относят целые типы, байтовые, символьные и логические.
Новые (пользовательские) типы данных нужны в первую очередь для наглядности и удобства:
const n = 10; m = 50; type matr = array [1..n, 1..m] of integer; var a : matr;
Но другой причиной является то, что в Паскале в определенных конструкциях разрешено записывать лишь имена типов, а не их определения.
Например, при описании процедур с параметрами считается неверным писать:
procedure p(a: array[1..10] of Integer);
Зато следует создать тип данных и записать так:
type vector = array[1..10] of integer; var procedure p(a: vector);
Примеры описания массивов при помощи новых типов
Три варианта описаний матрицы эквивалентны:
type vector = array[1..10] of integer; matritsa = array[1..8] of vector;
type matritsa = array[1..8] of array[1..10] of integer;
type matritsa = array[1..8,1..10] of integer;
В следующем примере переменные c и d описаны одинаково:
type vector = array[1..10] of integer; matritsa = array[1..8] of vector; var a,b: vector; c:matritsa; d:array[1..8] of vector;
Пример результата:
Перечисляемый тип и интервальный тип в Паскаль
Перечисляемый тип
В программе можно использовать переменные такого типа, который не совпадает ни с одним из стандартных типов.
Рассмотрим пример создания перечисляемого типа в Паскаль:
Интервальный тип определяет конкретное подмножество значений, которые может принимать данная переменная. Создается путем задания наименьшего и наибольшего значения порядкового типа. В качестве констант (минимального и максимального значений) могут использоваться значения любых простых типов кроме вещественных типов.
Рассмотрим пример объявления интервального типа:
Совместное использование перечисляемого и интервального типов
Переменную интервального типа можно задать с основой на базовый перечисляемый тип. Рассмотрим пример:
type color=(red,yellow,green,blue); var b:red..green; begin b:=red; writeln(b); b:=yellow; writeln(b); b:=green; writeln(b); readln end.
В данном примере тип color — является базовым. Переменная b интервального типа определена при помощи базового перечисляемого типа.
Один из вариантов решения данной задачи выглядит так:
Код данного решения обладает не самой лучшей наглядностью, к тому же приходится самому вычислять номера месяцев начала и конца лета (6 и 8).
Удобство и наглядность таких программ можно повысить следующим образом:
TYPE mes = (january, february, march, april, may, june, july, august, september, october, november, december); CONST dni:array[january..december] of Byte = (31,28,31,30,31,30,31,31,30,31,30,31); VAR s:Integer; i:mes; <переменная счетчика цикла i задана типом mes, а не Integer>BEGIN s:=0; for i:=june to august do s:=s+dni[i]; WriteLn(s) END.
Использование интервального типа в качестве диапазонного ограничителя
Следующий пример продемонстрирует неправильное решение данной задачи:
var otpravlenie,pribitie:byte; begin otpravlenie:=22; pribitie:=otpravlenie+10; writeln(pribitie) end.
программа вместо ответа «8» напечатает ответ «32».
Введение ограниченного диапазонного типа позволит избежать неправильного результата, однако компилятор все равно выдаст ошибку:
var otpravlenie,pribitie:0..24; begin otpravlenie:=22; pribitie:=otpravlenie+10; writeln(pribitie) end.
Зная об ошибке, теперь можно ее исправить.
type znak=(oven,lev,strelets,vesi,vodoley, bliznetsi,rak,skorpion,ribi,kozerog,telets,deva); var a:znak; begin a:=lev; if a =vesi) and (a =rak) and (a =kozerog) and (a
2. Пользовательский тип данных
2. Пользовательский тип данных
Пользовательский тип данных отличается от всех базовых типов тем, что он не был изначально вшит в систему управления базами данных, он не был описан как тип данных по умолчанию. Этот тип может создать для себя любой пользователь и программист баз данных в соответствии с собственными запросами и требованиями.
Таким образом, пользовательский тип данных – это подтип некоторого базового типа, т. е. это базовый тип с некоторыми ограничениями множества допустимых значений.
В записи на псевдокоде, пользовательский тип данных создается с помощью следующего стандартного оператора:
Create subtype имя подтипа
Type имя базового типа
As ограничение подтипа;
Итак, в первой строчке мы должны указать имя нашего нового, пользовательского типа данных, во второй – какой из имеющихся базовых типов данных мы взяли за образец, создавая собственный, и, наконец, в третьей – те ограничения, которые нам необходимо добавить в уже имеющиеся ограничения множества значений базового типа данных – образца. Ограничения подтипа записываются как условие, зависящее от имени определяемого подтипа.
Чтобы лучше понять принцип действия оператора Create, рассмотрим следующий пример. Пусть нам необходимо создать свой специализированный тип данных, допустим, для работы на почте. Это будет тип для работы с данными вида чисел почтового индекса. От обычных десятичных шестизначных чисел наши числа будут отличаться тем, что они могут быть только положительными. Запишем оператор, для создания нужного нам подтипа:
Create subtype Почтовый индекс
Type decimal (6, 0)
As Почтовый индекс > 0.
Почему мы взяли именно decimal (6, 0)? Вспоминая обычный вид индекса, мы видим, что такие числа должны состоять из шести целых чисел от нуля до девяти. Именно поэтому мы и взяли в качестве базового типа данных – десятичный тип.
Любопытно заметить, что в общем случае условие, накладываемое на базовый тип данных, т. е. ограничение подтипа, может содержать логические связки not, and, or и вообще быть выражением любой произвольной сложности. Определенные таким образом пользовательские подтипы данных могут беспрепятственно использоваться наряду с другими базовыми типами данных и в программном коде, и при определении типов данных в столбцах таблицы, т. е. базовые типы данных и пользовательские при работе с ними совершенно равноправны. В визуальной среде разработки они появляются в списках допустимых типов вместе с другими базовыми типами данных.
Вероятность того, что нам при проектировании новой собственной базы данных может понадобиться недокументированный (пользовательский) тип данных, достаточно велика. Ведь по умолчанию в систему управления базами данных вшиты только самые общие типы данных, пригодные соответственно для решения самых общих задач. При составлении предметных баз данных без проектирования собственных типов данных обойтись практически невозможно. Но, что любопытно, с равной вероятностью нам может понадобиться и удалить созданный нами подтип, чтобы не загромождать и не усложнять код. Для этого в системах управления базами данных обычно встроен специальный оператор drop, что и означает «удалить». Общий вид этот оператор удаления ненужных пользовательских типов имеет следующий:
Drop subtype имя пользовательского типа;
Пользовательские типы данных, как правило, рекомендуется вводить для подтипов достаточно общего назначения.
Данный текст является ознакомительным фрагментом.
Продолжение на ЛитРес
Читайте также
Пользовательский интерфейс и инструментарий программы
Пользовательский интерфейс и инструментарий программы После запуска программы на экране отображается ее пользовательский интерфейс, который показан на рис. 4.1. Рис.
2.3. Пользовательский интерфейс
2.3. Пользовательский интерфейс Помимо получения необходимой информации отладчик должен предоставить ее в удобном для пользователя виде. Для этого служат интерфейсные команды и функции.Интерфейс отладчика состоит из:• графического интерфейса;• режима комадной
3.3. Пользовательский интерфейс
3.3. Пользовательский интерфейс При анализе данных или профилировании важную роль играет представление полученной информации. Как и в случае активной отладки пользовательский интерфейс делится на три составляющих: графический интерфейс, режим командной строки и
Глава 2. Графический пользовательский интерфейс
Глава 2. Графический пользовательский интерфейс До середины девяностых существовали отдельно компьютерная графика и отдельно – настольные игры в компьютерную графику. Помнящие историю отечественной школьной информатизации читатели, возможно, сталкивались с чудом
Пользовательский контент
Пользовательский контент Практика показывает, что при правильной организации сервисов и большой посещаемости пользовательский контент становится самым дешевым и при этом привлекательным для новых посетителей. Уровень доверия к информации, представленной в
Глава 2. Графический пользовательский интерфейс
Глава 2. Графический пользовательский интерфейс До середины девяностых существовали отдельно компьютерная графика и отдельно — настольные игры в компьютерную графику. Помнящие историю отечественной школьной информатизации читатели, возможно, сталкивались с чудом
Глава 1 Пользовательский интерфейс и настройки системы
Глава 1 Пользовательский интерфейс и настройки системы • КОМПАС-3D V10: первое знакомство• Главное меню КОМПАС-3D V10• Компактная и другие панели инструментов• Настройка системы• Особенности последних версий КОМПАС-3D• РезюмеПрежде чем приступить к непосредственному
Глава 2 Пользовательский интерфейс AutoCAD
Глава 2 Пользовательский интерфейс AutoCAD На рис. 2.1 показан классический рабочий стол AutoCAD для Windows. Рис. 2.1. Рабочий стол AutoCADВ данный рабочий стол включены:• падающее меню Menu Browser – меню, появляющееся при щелчке кнопкой мыши на кнопке A в верхнем левом углу окна программы
Пользовательский интерфейс Access 2007
Пользовательский интерфейс Access 2007 Access 2007 имеет совершенно новый интерфейс, не похожий на интерфейс предыдущих версий программы. Новый дизайн упрощает нахождение нужных элементов управления и делает работу с программой более простой и комфортной.В предыдущем разделе
Пользовательский интерфейс редактора Visual Basic
Пользовательский интерфейс редактора Visual Basic Редактор Visual Basic является стандартным блюдом Microsoft- меню, панели инструментов и комбинации клавиш выглядят и работают очень похоже на Microsoft Office. Вы будете чувствовать себя как дома, если используете VBA с приложениями из Office.С
Глава 2 Пользовательский интерфейс AutoCAD
Глава 2 Пользовательский интерфейс AutoCAD На рис. 2.1. показан классический рабочий стол AutoCAD для Windows. Рис. 2.1. Рабочий стол AutoCADВ данный рабочий стол включены: • падающие меню – верхняя строка, расположенная непосредственно под заголовком окна программы;• необязательные
Глава 2 Пользовательский интерфейс AutoCAD
Глава 2 Пользовательский интерфейс AutoCAD Падающие меню Панели инструментов Стандартная панель инструментов Панель стилей Панель рабочих пространств Панель слоев Панель свойств объектов Строка состояния Окно командных строк Текстовое окно Экранное меню Функциональные
Глава 2 Пользовательский интерфейс AutoCAD
Глава 2 Пользовательский интерфейс AutoCAD На рис. 2.1. показан классический рабочий стол AutoCAD для Windows. Рис. 2.1. Рабочий стол AutoCADВ данный рабочий стол включены:? падающие меню Menu Browser – меню, появляющееся при щелчке на кнопке A в верхнем левом углу окна программы;? необязательные
Глава 2 Пользовательский интерфейс Flash
Глава 2 Пользовательский интерфейс Flash В этой главе мы изучим основные принципы работы с пакетом Macromedia Flash 8, без знания которых не обойтись. А именно, познакомимся с Flash, что называется, «лицом к лицу», изучим его интерфейс, многочисленные окна и панели, вспомогательные, но
Пользовательские типы данных в C ++
Типы данных — это средства для определения типа данных и связанных с ними операций по их обработке. Существует три типа типов данных:
В этой статье пользовательский тип данных объясняется:
Определяемые пользователем типы данных:
Типы данных, которые определяются пользователем, называются производным типом данных или определяемым пользователем производным типом данных.
Эти типы включают в себя:
Ниже приводится подробное описание следующих типов:
Синтаксис :
// C ++ программа для демонстрации
// Учебный класс
using namespace std;
// Объявление объекта класса вундеркиндов
// доступ к элементу данных
// доступ к функции-члену
Синтаксис:
// C ++ программа для демонстрации
// Структуры в C ++
using namespace std;
// Создать массив структур
struct Point arr[10];
// Доступ к членам массива
using namespace std;
// Декларация о союзе такая же как структура
// переменная объединения t
// Вы также получаете значение 2
cout «After making x = 2:»
// TX также обновляется до 10
cout «After making Y = 10:»
// Программа для демонстрации работы
// перечисления в C ++
using namespace std;
где тип — любой тип данных C ++, а имя — новое имя для этого типа данных.
Это определяет другое имя для стандартного типа C ++.
Пример:
// C ++ программа для демонстрации typedef
#include
using namespace std;
// После этой строки можно использовать BYTE
// вместо неподписанного символа
Развитие пользовательских типов данных в программировании
Хотелось бы остановиться и посмотреть на развитие языков программирования с точки зрения развития пользовательских типов данных (ПТД).
Сразу хочу оговориться, под пользователями понимаются программисты, как люди, пишущие код на этих языках. Ну, и те, кто этот код сопровождает или просто читает.
Пользовательские типы данных — это типы данных, которые могут быть созданы пользователем на основе того, что доступно в языке.
Пользователи желают иметь примерно такие типы данных
Пользователи хотели иметь возможность составлять данные так, как они сами того хотят. Хотели, хотят, и наверняка будут хотеть. Всё больше, всё разнообразней и сильнее.
Именно поэтому полезно проследить за развитием пользовательских типов данных в программах и языках программирования.
Отсутствие пользовательских типов данных
Когда-то пользовательских типов данных не было. Как-то выкручивались
На заре компьютерной эры языки были не ахти – машинные: или ты подчиняешься их диктату (а диктат там простой: или ты используешь малобитовые числа в двоично-десятичной системе исчисления (ну, или с какой процессор работает) и команды процессора) или нет.
Затрагивать те «тёмные века» мы не будем.
Одно можно сказать – пользовательских типов данных там не было, но программисты как-то выживали и как-то писали программы.
Встроенные типы данных
Встроенные типы такие удобные! Пока ими пользуешься так, как было запланировано разработчиками.
Первым более-менее нормальным языком был Ассемблер (на самом деле их много ассемблеров, мы говорим о костяке языков, который появился в 50х годах). Помимо читабельности, он принёс в себе много нового в плане пользовательских данных.
Самое крупное и неоспоримое достижение – это возможность создания переменных! С тех пор, почти во все языки вставлена такая возможность – возможность создавать переменные.
Второе не менее крупное достижение ассемблера, сделанное из благих побуждений, — попытка вставить в язык все типы данных (из тех, что возможно понадобятся программисту) прямо в язык.
Ну и остальное по мелочи – прежде всего, возможность записывать числа не только в двоичной, но и 16-ричной, восьмеричной, двоично-десятичной системах.
Тогда казалось, ну что ещё пользователю может понадобиться?
Шли годы, а необходимость не только в высокоуровневых абстракциях, но и в пользовательских типов данных всё росла.
И вот грянул 1957й год с Фортраном.
Написанный код на нём выглядел почти как современные языки, хотя его же вариант на перфокартах может подвергнуть в шок людей, которые захотят его читать.
Фортран дал всё что необходимо… для расчёта полёта баллистических ракет – такие данные как целые числа (типа int), с запятой (типа float), и комплексные.
Строки? Кому они нужны на перфокартах? Они в Фортране появятся позже – настоящие строки только через 20 лет, а их имитация – через 10 (вместе с логическим типом данных).
А ещё Фортран дал почти-настоящий пользовательский тип данных как массив (хотя его использование несколько отличается от современного), более детально об этом мы поговорим в главе о Групповых пользовательских данных.
Но пользователям мало — они хотят ещё и ещё пользовательских данных.
И вот появляется он – Алгол, уже в 1958, программы на котором легко читаются и в наши дни.
Вот как раз Алгол принёс основу того, что сейчас есть везде — булевские типы, строковые, разнообразные целочисленные типы и числа с запятой. Немногим позже всё это применит и Фортран, но Алгол был пионером.
Казалось бы — все аппетиты удовлетворены, какие ещё типы необходимы пользователям? Да всё уже реализовано — только бери и пользуйся.
И тут наступая на пятки Алголу с Фортраном, в 1958 появился ещё один, совершенно непохожий на язык язык – Лисп.
Лисп может сделать невообразимые функции. Только как с этим жить?
Он дал ещё один, совершенно новый тип данных, настоящих пользовательских типов данных – функции (вида С-выражений), прочно начавших входить во все современные языки только с начала 21 века (прежде всего, благодаря мультипарадигменным языкам Питон и Руби). Если учесть, что Лисп даёт оперировать макросами – что-то вроде программирование eval-ами (в 58-то) – неудивительно, что мир был к нему не готов.
А вот готов ли сейчас мир к Лиспу? Наверное, нет.
Заострю внимание, почему. Лисп, как и любое другое функциональное программирование, работает с сильно-взаимосвязанными объектами, чем-то напоминает сцепленные шестерёнки механических часов. В отличие от императивных языков, любое вклинивание в шестерёнку – стопорит весь механизм. Из-за этого, требования к языку, в том числе и к пользовательским типам данных значительно строже. Те, проблемы, которые возникли у Лиспа сразу же, у императивных языков они обострились лишь к концу 80х годов.
Лисп даёт возможность построить любые С-выражения. Но он даёт лишь один инструментарий – стандартный и простой инструментарий работы с С-выражениями.
Получается, есть возможность написать любые пользовательские данные, но работать с ними можно лишь как с примитивами. Развитие Лисп-подобных языков показало что пока хороший инструментарий не найден для ненаписанных С-выражений.
Недолго все были довольны. На почти десятилетие замедлилось развитие пользовательских данных.
Если что-то и хотели, то допиливали эти типы данных прямо в язык. Но в те времена языки развивались и обновлялись не так быстро как сейчас, поэтому в основном занимались имитацией создания пользовательских типов данных.
Имитация от оригинальных типов данных почти не отличается по функциональности. В основном в те времена имитация основывалась на эмуляции функциями (процедурный стиль).
Главное отличие от настоящих пользовательских типов одно – если захочется «немного» изменить тип данных, придётся переписывать весь функционал. Это никому не хотелось делать.
Поэтому и имитировать стали гибче.
Сейчас скажу то, что знают все, однако тут важна не сама описываемая техника имитации, а угол зрения на эту технику.
Иногда надо работать только с теми возможностями, которые есть
Вначале появился конфигурируемый функционал на основе флагов. Это не самый простой метод, зато он очень сильно напоминал ассемблер, а его тогда ещё знали.
Суть передачи флагами проста — передача числа как параметра, число же представляется как двоичный ряд флагов. Несмотря на широкий способ использования, до сих пор в языках нет специальных типов данных вроде ряда флагов. Вместо этого нашли хороший заменитель – именованные константы и битовые операции, выглядящие как логические.
Наиболее простой метод – он до сих пор широко применяется везде – конфигурирование параметрами: чем больше надо конфигурировать, тем больше параметров передаётся. Один только имеет минус – слишком легко запутаться в порядке передачи параметров. Поэтому стараются такими методами передавать до 4-5 аргументов.
Для большего количества аргументов, с развитием настоящих пользовательских данных, прежде всего групповых и композитных, появилась возможность передавать один сложный аргумент – это то же самое конфигурирование только не горизонтально, а вертикально.
Развитием этого метода можно назвать создание встроенных языков (DSL) для конфигурирования функций.
Третий метод гибкости имитаторов — это изобретение манипуляторов (handler-ов), хотя в то время они так не назывались, и зачастую они были суррогатами манипуляторов — представляли собой числа и строки, а не ссылки или указатели.
Заканчивалась эра встроенных типов данных.
Но наступил 1972 год, появился… Си. И эра господства динозавров (встроенных типов данных) продолжилась ещё на одно десятилетие, хотя пользовательские типы данных начали отвоёвывать своё место под солнцем.
В том числе и в самом Си.
Но пока вернёмся к ещё одному встроенному типу данных, ставшим одной из причин роста популярности языка. Си ввёл низкоуровневый тип данных, который был в ассемблере, и начисто был забыт в первых высокоуровневых языках — динамические типы. Прежде всего, это ссылки(reference) и указатели(pointer). К ним добавился обслуживающий нулевой тип данных – null.
Динамические типы данных как отмычки — неказистые, зато в какие только потаённые комнаты нельзя с ними попасть?!
Ссылку можно рассматривать как один из вариантов реализации такого типа пользовательских данных как синоним.
Развитие синонимов можно найти в ПХП и его концепцией переменных переменных, когда можно вернуть значение переменной или функции, имя которой записано в значении вызываемой переменной.
В Си вызов функции можно вызвать фукнцией-вызовом (call), или можно передать функцию обратного вызова — callback.
Вдобавок к этому, динамические типы данных помогают хорошо ускорить выполнение скомпилированного кода.
К этим плюсам у динамических типов данных есть ещё один огромный плюс – с ними достаточно просто можно реализовывать то, что не было заложено в самом языке. Лишь одно сокрушает, к написанному с их использованием можно обращаться только с помощью инструментария работы с динамическими данными. Но технику обхода при имитации данных известна – замкнуть в функциях и возвращать ссылку/указатель на созданное – манипуляторы (handler). Манипуляторы — один из тех типов данных, в разних языках которых он может называться совершенно по-разному.
Например, в ПХП они называются ресурсами, в Эрланге — портами и идентификаторами процессов. В Коболе есть такой тип данных как файл и картинка.
Однако динамические типы данных – не только одни плюсы. Минусы тоже есть, порой очень большие.
Чем больше свободы в использовании динамических типов данных даётся языком, тем:
1) увеличиваются возможности по созданию того, чего не было заложено в языке (и не обязательно это позитивные возможности)
2) компилятор всё меньше вмешивается в действия пользователя, и вся ответственность за действия ложится на программиста
2) резко увеличивается небезопасность кода
3) резко увеличивается возможность инъекций в пространство имён и значений
4) сборщик мусора всё меньше вмешивается, и вся ответственность ложится на пользователя
Дальнейшая история показала, что создатели последующих языков (или при добавлении к уже существующим языкам) при добавлении динамических данных балансировали межу безопасностью и возможностями.
Близился конец 70х, и эти базовые встроенные типы данных стали уходить в периферию, в рутину, уступая место настоящим пользовательским данным.
Однако реальность преподносит иногда удивительные сюрпризы.
Кто знает, сколько ещё можно найти в старых-добрых и давно понятных типах данных?!
Иногда надо просто увидеть новое там, где всё давно известно. Как, например, эта сортировка М&Мs
Тогда, в конце 70х появился скриптовый язык AWK (использовавший разработки утилиты grep), а, десятилетием позже на его основе, в 1987 появился такой язык как Перл. И среди прочего у него был (до сих пор есть) такой экзотический встроенный тип данных, как регулярные выражения.
Перл помог взглянуть на такой старый тип данных как строки с новой стороны. Инструментарий по работе с ним в ранних языках можно было рассматривать как упрощённые парсеры.
Языки регулярных выражений оказались очень гибким и супер-мощным инструментарием для работы с символьными типами данных.
Групповые типы данных
Некоторые группы большие. Хочется с ними быстро работать
По сути, групповые типы данных – это много чего-то того, что есть в языке, как правило мономорфная группировка. Зачастую эти типы данных не пользовательские, а встроенные, однако они порой столь гибкие, что этого бывает достаточно.
Уже Фортран поддерживал групповые типы данных – это массивы (array), хотя они выглядели немного не так, как сейчас. Массивы, очень похожие на современные были уже в Алголе. В Паскале были множества (set)
В Лиспе были списки (list).
Потом появились хеш-таблицы, ассоциированные массивы, вектора, кучи, очереди, стеки, деревья…
Что-то встраивалось, что-то имитировалось или создавалось с помощью пользовательских типов данных.
Дальнейшее развитие групповых типов данных привело к 2 различным веткам развития
1) необходимость использования своего функционала с каждым групповым типом данных было не самое удовлетворительное, хотелось работать с ними единообразно. Основным инструментарием для этого в императивных языках являются коллекции и итераторы. В основном были добавлены в ранние 2000е.
2) В 80е годы с развитием роста данных, необходимость в расширении инструментария работы с групповыми типами данных росла как на дрожжах. Появились базы данных, а с ними и запросы и языки запросов (query language). В середине 80х Стуктурированный язык запросов (SQL) становится доминирующим. Как и парсеры для строк, язык запросов дал понять, что инструментарий, который использовался для групповых типов данных можно рассматривать как примитивный язык запросов. Базы данных, как правило, вынесены из языка, а в языке существуют лишь методы работы с ними, поэтому их нельзя считать полноценными пользовательскими типами данных. Хотя из-за их гибкости, это несущественно.
Настоящие пользовательские типы данных
В эру встроенных типов данных в разных языках можно найти поддержку весьма экзотических встроенных типов данных.
Например, в Си таким типом данных было перечисление (enum). Его до Си, впервые вводит Паскаль (1970), хотя и называл скаляром.
Что такое перечисление легко объяснить даже детям на пальцах
Перечисление — самый настоящий пользовательский тип данных! Казалось бы, надо Си поставить памятник за это. Нет, разве что надгробный.
Дело в том, что пользователи имеют возможность построить любые перечисления. Только вот для работы с ними в Си нет ничего (в Паскале был базовый набор). Вообще ничего. Создавать перечисления можно, а работать с ними — нельзя.
Поскольку Си был в мейнстриме, мало кто хотел добавлять этот тип данных в другие языки. Только в Си++11 появился хоть какой-то инструментарий по работе с перечислениями.
Этот пример, как и развитие Лиспа, показал, насколько важно иметь не только пользовательские типы данных, но и инструментарий работы с ними.
Композитные типы данных
Записи такие разнообразные. Ещё бы научится как ими пользоваться
Зато в Си был другой настоящий пользовательский тип данных. Хотя его придумали значительно раньше – ещё в Коболе, вышедший в ранние 60е (сам язык создан в 1959).
Это запись (record), в Си называется структурой (struct).
Запись – не что иное, как группа разнородных типов данных.
К записям прилагается инструментарий с работой с записями. Например, Си не в полном объёме даёт стандартный ныне минимум работы с записями (например, лишь однобокая инициализация).
С записями легко уже не сымитировать, а создать настоящие списки и деревья.
Неужели, снова всё есть в языке?
Нет, и ещё раз нет.
Уже мало иметь просто пользовательские типы данных. Мало иметь инструментарий работы с ПТД как с ПТД. На первый план выходит другое.
Никто не даёт никакого инструментария для несозданных типов данных!
Теперь уже языки с поддержкой записей попались в похожую ловушку на ту, в какую попал Лисп – свои данные создавать можно, а работать с ними – только базовый набор.
Только у Лиспа ситуация хуже: в этом языке всё является С-выражениями, а в Си, Коболе, и других, запись – самостоятельный тип, к тому же у него есть свой, хоть и небольшой, инструментарий.
Благо, решение этого тупика был давно известен — имитация работы с пользовательскими типами данных с помощью функций.
Именно благодаря записям/структурам (в том числе и в Си), пришло к программистам осознание важности пользовательских типов данных.
При этом явно обозначилась острая нехватка инструментов по работе с ещё не созданными типами данных.
А ответ был. Как ни удивительно звучит, но был ещё за пару лет до создания Си, он был в Европе, и назывался Симула (1967). И тогда, когда Си начал задыхаться в нехватке инструментария пользовательских типов данных, Си++ (в 1983) перенял всё лучшее у Симулы и применил к Си синтаксису.
Объекты
Объекты могут всё делать. Сами и над самими собою
Объекты(object) – ещё один тип пользовательских данных. Он обладает значительно большими возможностями, чем было у записи.
Это дало ему возможность завоевать просто бешеную популярность.
По иронии доли, как и Симула, вышедшая за пару лет до С, в котором не было объектов, так и за пару лет до Си++ Смолтолк в 1980 объявил парадигму «всё объекты».
Но Смолтолк не завоевал большой популярности, ему пришлось ждать, пока Си++ дойдёт до уровня стагнации, и только после этого, Ява в 1995 вновь смогла поднять парадигму «всё объекты» гордо над головой.
Чем же так хороши объекты, ведь они не так уж сильно отличаются от записей. По сути – те же записи с добавлением методов.
Во-первых, инструментарий по работе с самими объектами значительно богаче и сильней, чем инструментарий по работе со структурой.
А во-вторых, инструментария по работе с ещё не созданными объектами… тоже не было.
Стоп, спрашивается, где же тут «во-вторых», если и у объектов нет никакого инструментария с несозданными типами данных, и у записей нет. И, тем не менее, во-вторых! Для записей необходимо было имитировать этот инструментарий, в то время как у объектов можно просто реализовать этот инструментарий внутри самого объекта!
А если вдруг надо было немного изменить пользовательский тип данных, удобно было с помощью инструментария объектов — наследованием создать потомка, и в нём исправить поведение.
Бум и тотальное использование объектов привело ныне к стагнации.
Что же сейчас мешает объектам и дальше развиваться?
Как мы помним, реализация инструментария нового объекта полностью лежит в ответственности у программиста, а не на языке, поэтому уровень переиспользования кода не такой большой, как мог бы быть.
Не менее важна и всё большая закрытость. Объект сам всё будет делать, хотя и делать всего от него почти никогда не нужно. И наоборот, обладая возможностью всё делать самому, другим этот объект ничего делать не будет. Может, но не будет.
Частично проблему помогает решать введение интерфейсов (interface) и примисей (mixins, traits).
Интерфейсы впервые вводят Делфи (в 1986 ещё как Объектный Паскаль), позже Ява и Си#. И это понятно – они были лидерами в объектных языках.
А вот что удивительно, примести/трейты появились при попытке присоединить объекты к Лиспу (Flavors, CLOS) (CLOS — часть Коммон Лиспа), позже добавлены в разные языки.
Однако даже такие абстрактные помощники, как интерфейсы и примеси не всегда помогают, например, к старому объекту с меткой «финализированный» (final). Частично можно решить проблему гибридизации на основе прототипного наследования (открытого языком Селф(диалектом того самого Смолтолка) в середине 1980х и получившего популярность благодаря, прежде всего, ЯваСкрипту десятилетием позже), однако у этого метода есть свои минусы относительно классового наследования.
Интересна поддержка метаклассов (metaclass), которые были заложены в Смолтолк ещё в 1980 и ныне поддерживаются некоторыми языками, например, Питоном. Метаклассы работают с классами как объектами (такой себе рекурсивный подход). Это сильно улучшает инструментарий работы с объектами как объектами.
Ныне на первое место приходит не создание нового объекта, а грамотный подход к проектированию системы с использованием шаблонов(паттернов) проектирования.
Что будет дальше? Вопрос риторический.
Есть ли альтернативы столь мощным пользовательским типам данных, как объекты, структуры? Есть, и даже лучше, чем объекты! Стоит взглянуть на них получше, что бы понять, как могут в дальнейшем развиваться объекты.
Поиск альтернативы
Христос и Кришна вместе. Императивность и Функциональность могут быть вместе
Куда бы посмотреть в поисках альтернативы?
Декларативные (типа HTML) и логические языки (типа Пролога) на сегодня альтернативы не содержат. Они основаны на том, что вместо программиста работает компилятор/интерпретатор.
И тут надо либо
1) просто бросить попытки добавить пользовательские типы данных и войти в симбиоз с другим языком (например HTML + ЯваСкрипт)
2) подключить другие парадигмы программирования.
Кстати, на счёт подключения других парадигм, казалось бы, чем хорошо иметь мультипарадигменные языки? Питон (1991) и Руби (1994) так не думали.
И, оказались правы. Там, где легко уделывал всех Лисп – удобно применять парадигму функционального программирования, где нужна простота — там процедурный стиль, на остальные случаи — хорошо справляются объекты.
Казалось бы, никаких новых пользовательских данных не добавилось, а эффективность написания кода сильно возросла.
И вот когда на дворе 2011, даже в Си++ пришли лямбда-функции из Лиспа.
Из Явы выросла Скала с 2003, приняв посылку, что объекты — это ещё и функции.
Метапрограммирование – это хорошо. Но, как правило оно позволяет либо расширить(сделать гибче) встроенные типы данных, либо для порождения нового кода или просто возможности создать обобщения, работает с встроенными типами данных. Пока что только со встроенными. Но даже без пользовательских типов данных, за метапрограмированием будущее(а уже во многом и настоящее) в плане избавления от рутины программирования.
Алгебраические Типы Данных
Ух ты, как грозно звучит!
Стандартный МЛ в 1990 впервые вводит Алгебраические Типы Данных (АТД).
Алгебраические типы данных – это один из самых мощных пользовательских типов данных. Найдены они были математиками Хиндли и Милнером в лямбда-исчислении.
АТД — это «всё в одном» — унитарные типы данных (типа null), перечисление (типа enum, bool), защищённые базовые типы (типа как resource в противовес ссылке), переключательные типы (что-то вроде в Си — union), спископодобные структуры, древовидные структуры, функции, кортежи, записи, объекто-подобные структуры (но не объекты). И любые комбинации всего этого.
Это значительно лучше объектов! По крайней мере, с точки зрения создания разнообразных самих пользовательских типов данных – да, лучше, безусловно.
Только с других точек зрения этот тип данных как в Стандартном МЛе, так и в более позднем ОКамле – весьма и весьма скуден.
Инструментарий для работы с АТД как с АТД не сильно больше, чем работой с записями, и значительно меньше, чем с объектами.
Во-вторых, инструментария для работы с ещё несозданными типами данных нет. И, в отличие от объектов, прятать самописный инструментарий некуда. Только имитировать.
Камль, как потомок Стандартного МЛ, недолго думая, добавил к себе объекты и стал ОКамлем (к 1996му). И параллельно ОКамль начал развивать альтернативное, более функциональное решение, куда спрятать реализацию инструментария по работе с пользовательскими данными – в параметрические модули. И, за лет 15 ОКамль построил достойную функциональную замену объектам. Тут ещё интересен подход – как мы помним, параметрические модули-функции стали вводить для того, что бы избавиться от проблемы отсутствия инструментария для ненаписанных данных АТД, только вот сами параметрические модули ныне почти не отличаются от АТД, а значит, сейчас нет инструментария для ещё не написанных… модулей. Прямо рекурсия!
А за эти 15 лет ОКамль находит ещё один вариант решения проблемы отсутствия инструментария по несозданным типам данных, он их так и называл — вариант (variant). Это иной, не АТД тип данных, хотя внешне похож. Это переключательный тип данных, заодно им можно делиться в любых пропорциях, включая смешивание (АТДанными делиться в пропорциях нельзя, равно как и смешивать). Хорошо, что можно пропорционально делиться (с объектами можно добиться лишь с помощью интерфейсов или трейтов, да и то – не полностью), зачастую заманчиво смешивание (такое с объектами не сотворишь), плохо, что в любых пропорциях. Тут есть ещё над чем работать. Этот тип данных недоразвит. Из простых путей развития – добавить в доселе скудный инструментарий работы с вариантами инструментарий множеств.
Алгебраические Типы Данных в паре с Классами типов
АТД вместе с классами типов выглядят грубо, но способны на многое
В начале 90х на основе Стандартного МЛ, а так же нескольких академических языков, был разработан ещё один язык, впервые стандартизированный лишь в 1998. Им был Хаскель.
У него были те же Алгебраические типы данных, что у Стандартного МЛ и ОКамля. И такой же скудный набор по работе с АТД как с АТД. Но у Хаскеля было (и до сих пор есть) то, чем не обладал доселе ни один тип пользовательских данных. У Хаскеля есть инструментарий для ещё ненаписанных пользовательских типов данных – это классы типов (class).
Сами классы типов представляют собой что-то вроде интерфейсов или примесей, наиболее близко к ролям (roles) в Перле, только введение классов отличается от введения интерфейсов/трейтов.
Интерфейсы для простых классов типов, примеси – для усложнённых.
Причём для сложных классов аналогия с примесью уже не будет подходить. К сложным классам нет необходимости подключать полностью данные, достаточно присоединиться в точках входа, или в одной из нескольких точек входа, если это разрешено.
Причём с помощью реализации класса (instance) можно делиться инструментарием, причём не только с кодом, написанным позже создания класса, но и для данных, созданных ранее (как если можно было добавить поведение финализированным объектам). Если брать аналогию в объектных языках, это достигается, прежде всего, путём присоединения не интерфейсов и примесей к объектам, а наоборот, объектов к примесям и интерфейсам (частично это уже есть у ролей в Перле).
Но Хаскель не остановился даже на этом. Он реализовал автоматическое выведение (deriving) классов для различных типов данных.
Это уже однозначно значительно больше по возможностям пользовательских типов данных, чем заложено в объектах.
Хаскель бурно развивается. Уже сейчас Алгебраические Типы Данных – лишь часть большего.
Ныне вполне можно создать семьи данных, Обобщённые Алгебраические Типы Данных (GADTs), экзистенциальные, многоранговые.
Классы типов тоже не стоят на месте. Уже сейчас можно использовать многопараметрические, функционально-зависимые, разновидовые классы.
Расширяется инструментарий работы с АТД как с АТД в контексте метапрограммирования.
Многое из этого нового существует как расширения языка, а в 2014 году это может войти в стандарт языка.