Языки программирования, 15 лекция (от 24 октября)
Материал из ESyr's Wiki pages.
Языки программирования 24.10.06
Вставочка в конец 4 главы:
Та форма, которая принята в совр ЯП, не единственна. Форма, принятая в Си шарп, основана на динамическом контроле типов.
До этого рассм позиционное соответствие параметров. То есть у каждого параметра есть свой номер, и досутп осущ по нему.
Есть ещё ключевое соответствие, где доступ осуществляется по имени.
Пример Ада:
procedure P(A, B, C:T);
можно ввести X, Y, Z:T
и при позиционном обращении
P(X, Y, Z) и A соотв X, и т. д.
Но в Аде есть ещё ключевое соответствие. Можно указать:
P(A=>X, B=>Y, C=>Z);
порядок в этом случае совершенно не важен
P*(B=>Y, C=>Z, A=>X)
Клбючевое соотв есть ещё в VB.
Можно пойти дальше и ещё задать умолчания. Например, это есть в C++. В случае, если параметр отсутствует, подставляется значение по умолчанию.
Void f(int x, int y=1);
параметры по умолч должны быть последними. Тогда можно вызывать
F(0), который соотв F(0,1)
это приводит к переменному списку параметров.
Это спорная практика.
Фактически, получается две функции:
f(int);
f(int, int);
и используется механизм перекрытия имён.
Понятно, что параметры по умолчанию можно проэмулировать путём задания нескольких функций: f(int x) {f(x, 1);}
Задание всякого рода умолчаний считается дурным тоном. Например, умолчания активно использовались в PL/1, и это один из его недостатков, так как эти умолчания зависили от окружения. Поэтому при проектировании ЯП старались избавляться от разного рода умолчаний.
В С++ Страуструп описывает ситуацию о стандартизации С++. Хорошо выверенное, локальное, удобное предложение не прошло. Примером такого предложения является предложение MS о ключевых параметрах, в дополнение к ключевым парампетрам.
Основная позиция комитета по стандартизации – необязательность этого. Это удобно, но технологическая потребность некритична.
Совр ЯП не поддерживают ни ключевые параметры, ни параметры по умолчанию.
Почему в VB это есть:
Если пытьаться использовать OLE-автоматизацию, суть в том, что специальные программы могут выступать как серверы OLE-автоматизации. Компоненты в Windows предст собой OLE-серверы, и они предлагают набор функций. Популярные программы: Word. Лектор, когда писал свою программу, использовал Word для проверки правописания. Можно вызвать метод checkspelling, в общем случае у него порядка 10 параметров. Естественно, что в большинстве случаев программист задаёт два-три параметра, осталные по умолчанию.
Лектору это напомнило опыт программирования на Фортране, исп стандартной библиотеки. Там основным способом передачи данных между модулями – через параметры. И там у функции бывало по 10-15 параметров. Наличие подобных интерфейсов свидетельствует о плохом проектировании.
Прав Вирт, Когда утверждает, что сложность языка переходит на сложность создаваемых проектов. Недостатки дизайна нивелируются дополнительными языковыми конструкциями.
Простые языки – Оберон, Модула-2 – на них нет сложных систем. Сложные системы строятся на сложных языках.
Глава 5.
п.1. Понятие типов в традиционных ЯП. Уникальность типа.
Чем по большому счёту отличаются традиционные ЯП от ОО. ОО опираются на традиционную, но немного отличаются. ОО отличаются понятием типа.
Понятие типа опирается на 4 аксиомы уникальности типа:
Каждый объект данных принадлежит единственному типу данных. - самое сильное ограничеиние традиц ЯП
У каждого типа есть имя, для анаонимных – неявное. Например, когда в Паскале описываем var A : array [1..N] of integer; 1..N – анаонимный тип. В Аде это два типа. Типы считаются различными, если их имена различны. Эта аксиома носит назван ие именная эквив типов
Каждая операция над типом данных имеет фиксированный тип и порядок операндов. Понятие типа характеризуется набором операций.
Разные типы данных несовместимы по операциям.
Вспе 4 аксиомы уточняют понятие строгой типизации. Чем строгая отличается от сильной, нигде не сказано. Аналогично со слабой типизацией. В любых языках со строгой или сильной типизацией выполняется 1 аксиома. Например, скриптовые языки не удовл это аксиоме – там есть понятие variant – некий тип, который является объединением типов.
2 аксиома – аксиома именной эквив типов. Рад языков от неё отходит. Например: Type T = T1. То есть, часто в ЯП вводится синонимия типов. В Си фактически typedef и есть определение синонима. Но если про неё забыть, то если типы не имеют синонимов, то они различны. В Аде пошли дальше: type T1 is new T . В некоторых ЯП существует структурная эквивалентность типов – типы эквивалентны, если их структура совпадает. Как только возникают ссылочные типы, то завдача может стать алгоритмически неразрешимой. Кроме того, имя важно само по себе, ибо тип хар-зуется набром операций и именем. Тем не менее недостатки языкового дизаяна разрушают концептуальную целостность. Во в Си структурная или именная целостность:
Struct A {int a, b; }
Struct B {int a, b; }
Это разные типы, и на первый взгляд именная эквивалентность типов. Но в Си раздельная независимая трансляция. И в этом случая стандарту удовлетв следующая ситуация:
В модуле 1 описана Struct A {int a, b; } void F(struct A a);
В модуле 2 описана Struct B {int a, b; } extern f(struct B b);
struct B b;
F(b);
Это полностью удовлетворяет стандарту, и компиляторы должны обрабатывать это правильно. И вылезает структурная эквивалентность типов. Дырка в том, что у нас раздельная независимая трансляция. Когда мы описываем атрибуты, мы можем их немного не так задать, но структурно так же. Но на уровне одного модуля структурная эквивалентность не проходит. Концептуальной целостности нет. Но в Си++ тот же самый механизм раздельной трансляции, но структ эквивалентности нет. Ошибка будет выдана, что не найдена функция с необходимым параметрами, на этапе линковки. Причём имя функция будет иметь невразумительное имя. Например, у Паскаля и Дельфи свой формат объектного файла, свой загрузчик и т. д., которые намного богаче, чем стандартные, которые писались для ассемблера, где просто указывается, есть имя или нет. Си++ использует ту же самую структуру объектного модуля. В первом варианте компилятора делалось С++ => C, и когда возникает ситуация перекрытия имён, возникает вопрос, как их транслировать в Си, где его нет. Был придуман способ name mangling (кодирование имён), который позволил контролировать типы данных при сборке. Была принята единообразная схема кодирования имён, где содержится информация об имени функции и её параметрах, и эти имена будут различны для разных struct A и struc B. Но это не будет работать на старых редакторов связей, которые обрезали идентификаторы до 6 символов, так как первый компилятор на фортране был для IBM 709, где машинное слово было 6 байт. В современных ОС есть свои форматы исолняемых файлов, и это позволяет использовать такие имена.
Любой современный ЯП удовл двум аксиомам.
А вот от четвёртой аксиомы многие отступают. Нпример, операция присваивания. Слева и вправа могут стоять разные типы. В Си правила преобр сложны, в Си++ ещё сложнее.
4 аксиома ослабляется тем, что в ЯП есть неявные преобразования. 4 аксиома нарушается часте всего засчёт неявных преобразований.
Язык Ада обладает наиболее строгой типизацией, так как удовлетворяет всем 4 аксиомам.
Чем больше онмер аксиомы, тем больше отступают.
Какие преимущества несё концепция уникальности ТД:
Каждая операция зафикс, смысл их извествен, операции несовместимы, поэтому поведение любого объекта данных легко прогнозируемы. Программы получаются как надёжные, так и эффективные. То же относительно контроля.
Проблемы:
Полиморфизм операции. 3 аксиома говорит, что каждый ТД харак своим набором операций. Даже самый первый ЯП, Фортран, был полиморфным. Более того, любой ЯП является полиморфным. Это справедливо в первую очередь для встроенных операций. Например, даже в асме ADD отвечает разным машинным кодам. Тип операции выводится из типов операндов. То есть все стандартные операции полиморфны. Совр ЯП как решают проблемф полиморфизма: с помощью перекрытия операций. Та мдопускается делать пользовательские полиморфные операции (в J и D – нельзя перекрывать стандартные операции, в C# и C++ - можно).
Ада: есть overloading – перекрытие операции – статический полиморфизм, то есть выбор производится статически, во время компиляции. Это недостаток. Совр ЯП допускают динамический полиморфизм. Интересно, что в О-2 отсутствует статич, и присутствует динамический полиморфизм.
Янус проблема – объекты реального мира играют разныке роли. Как решается проблема в традиционных ЯП – объединение типов.
И решение 1 проблемы перекрытием, и янус проблемы объединением, их проблема в том, что они статические. При этом отстутствует какой-либо динамический контроль.
Во придумал Вирт замечательнй язык М-2,, и он вынужден был ввести тип ADDRESS. Напримр, нужно написать механизм распределения динамической памяти. И лонично его писать на М-2. Там нет шаблонов, ... . И единственный способ сделать это – добавить тип ADDRESS. Про тип ADDRESS известно, что к нему приводим любой тип-указатель.
Что являлось разочарованием для Вирта: программисты используют ADDRESS чаще, чем надо. При этом сразу же ломается контроль типов. Что автоматически делает язык ненадёжным.
Именно поэтому пришла другая парадигма – процедурно-объектная.
П2. Логические модули и определение типов.
Ада, М-2, О-2, Дельфи.
Самые простые модели – М2, о2
Самая сложная – Ада.
Осн на понятиях:
Объявление типа
Понятие модуля
Объявление объекта данных
Объявление задаёт имя и структуру.
Во всех этих языках объявление начинается с type
type имя is/= record, array, new
А что, если не определить тип – получится АТД, то есть тип, у которого отсутствует структура.
Если структуру типов опуститть можно, то набор операций опустить нельзя.
Во многих языках понятие модуля очень широко, и может служить не только для определения данных, но и для констант и операций.
Кроме понятия логического модуля есть понятие физического модуля. В рпостых ЯП понятие физмодуля слит с логическим модулем, то же в Дельфи, а в Аде не так.
Пока будем считать, что физмодуль один, но он разбит на кусочки.
При определении модуля главное – определить взаимодействие с другими модулями.
Рассмотрим Си. Всё, что сказал лектор, можно реализовать на Си, с помощью соотв технологии. Например, что является модулем на языке Си – файл. Причём, у каждого модуля есть интерфейс, есть реализация. На Си интерфейс в хедере.
Типичный пример – stdio.h
Тип FILE – пример АТД.
Чем плох язык Си – соотв технология должна реализовываться ручками, и никакого контроля нет. Но иногда так и приходится делать. Все системы защиты в Си – не от взломщика, а от дурака.
Поэтому особое внимание уделяется межмодульным связям.
Сама логичная концепция – в М2 и Дельфи
Есть понятие библиотечного модуля.
Вообще, модули деятся на следующие виды:
Главный
Библиотечный
Локальный
Главный модуль:
MODELU name;
объявления;
begin
выполнение;
end.
В аде два типа библиотечных модулей:
DEFINITION MODULE name begin end name. - только объявления
IMPLEMENTATION MODULE name объявления и определения подпрограмм и ... begin реализация end name. - объявоеия и реализация
Пример:
DEFINITION MODULE stacks
TYPE Stack = ...;
PROCEDURE Push(var S:Stack; X:Integer);
PROCEDURE Pop(var S:Stack):INTEGER;
PROCEDURE Peek(var S:Stack):INTEGER;
PROCEDURE IsEmpty(var S:Stack):BOOLEAN;
END stacks;
А в IMPLEMENTATION MODULE описываем полностью прототипы и их тела.
DEFINITION MODULE – список экспорта.
Если что-то оформлено в виде библиотечного модуля, ещё кому-то надо.
В Модуле 2 интерфейс и реализация находятся в разных физ модулях
В Паскале-Дельфи
Две части слиты в один физмодуль, но он разбит на две логические части, которые нельзя игнорировать вообще:
unit name;
interface
объявления
implementation
дополнительные объявления, реализация
begin
операторы
end.
В дельфи более кудрявая форма begin операторы
С точки зрения кода, где объектный код генерируется – он состоит из таблицу имён, объектный код реализации. И таблицы имён в implementation нет, так как они никому не нужны. А имена нужны только для того, чтобы ссылаться на них извне.
Top Speed Modula-2 – очень быстро транслировалась, и там def-файлы вообще не транслировалось, всё делалось на лету.
Как происходит взаимодействие (на примере Модулы-2):
Какова структура проекта
В первой реализации Модулы-2 даже был раздел EXPORT, где указывались экспортируемые имена.
Два вида видимости:
Потенциальная – видно при наличии уточнений. Например, именем модуля: M.name
Непосредственная