Языки программирования, 16 лекция (от 26 октября)
Материал из eSyr's wiki.
Языки программирования 26.10.06
В прошлый раз мы остановились на межмодульных связях.
Библиотечный модуль Модулы дыва состоит из двух подмодулей, связанных одним именем.:
Модуль-интерфейс, гнде только определения
Модуль-реализация. Реализация и дополн объявления для неё, а так же инициализирующая часть.
Дельфи:
физически представляет собой один модуль, но присутствует понятие ирнтерфейся и реализации, разница чисто синтаксическая. Есть более тонкие моменты, которые отличают, они связаны с механизмом межмодульных связей.
Не разрешаются вложенные моудли.
Возникают два понятия: потенциальная и непоср видимость.
Непосредственна – конгда имя можно употреблять без уточнЯюзей информации
Протенцияалдьная – с уточняющей, указание имени модуля через точку. Везде через точку, только С++ выпендрился, там ::
Непосредственно ыидимыми являются только имена модулей. Все остальные имена потенциально видимые только те, что в разделе определений. Имена из модулей-определений видимы потенциально. Из модуля определений осуществляется экспорт в глобальное пространство имён.
Все имена равноправны.
Кроме понятие экспорта, есть понятие импорта. Импорт может быть как в модуль определений, так и вмоудль реализации. Таблицпа имён из модуля реализаций – конкатенация таблицы из модуля поерделений.
Модуль реализации только импортироет, как из модуля определений (всегда, неявно), так и из других модулей.
Есть две формы импорта:
IMPORT список модулей.
Стандартные и пользовательские модули абсолютно равноправны, при этом реальный ждоступ получаем тогда, когда делуем предожение IMPORT. Оно должно быьб первым среди всех объявлений. С точки зрения реализации компилятор должен просмотреть все папки поиска, и подкачать соотв таблицы имён. При этом соотв имена в этом модуле, если в нём есть предложение IMPORT, все имена из него ст ановятся видимы потенциально. Приходиться писать InOut.WriteLn. Программистов это раздражает, поэтому есть другая формаи импорта: FROM имя модуля IMPORT список идентификаторов – и имена становятся видимы непосредственно. Это сделано в дань программистом, которые ленятся набивать длинные имена. Беда непоср импорта в конфликте имён. Причём могут конфликтовать как с именами других модулей, так и с локальными. Локальные имеют больший приоритет (граждане первого сорта имеют больше прав, относительно иммигрантов). Это достаточно удобная модель.
Отличия Дельфи:
Имена из модульной реализации недоступны никому, но есть едиственная форма предложения импорта: uses список модулей – импорт непосредственный. Если имена конфликтуют между собой, то в этом случае непоср видимост снимается.
С первой точки зрения этот подход более улдобен, но когда лектор столкнулся с программированием в Дельфи и Модуле-2, то на Модуле-2 текст читать приятнее. Ибо когда в uses полтора десятка модулей, то дрогадаться, из какого модуля идентификатор, трудно. Поэтому Борланд всегда поставлял ос всеми своими компиляторами grep. А в Модуле-2 мы обречены на успех при поиске по файлу.
Порядок загрузки модулей поерделяется порядком зависимостей. В момент загрузки доступна инициализация – begin ... end. .
В этих языках есть принцип – РОРИ, Разделение
Определения
Реализация
Использования
Главенствовал в ЯП в 70-ч годах, когда появились ЯП с чёткой модульной структурой.
Есть ли в Си разделение определния и реализации? Это можно сделать, но ручками. И есть много подводных камней. Например, каждый хедер начинается с ifndef, дабы избежать повторного включения модулей.
#ifndef SYMBOL
#define SYMBOL
...
#endif
Интересно с этрой точки зрения посмотреть на язык Оберон, что же он упростил. История идёт по спирали, и там от РОРИ с первого взгляда отказалисб.
В первой версии языка:
DEFINITION M
опр-я
ENDM.
MODULE M
ENDM.
Модно выкинукть понятия модуля определения как такового. Вместо этого можно просто помечать. То же, что и в ассемблере.
MODULE M;
TYPE T* - если после имени стоит звёздочка, то оно экспортируемое
REOCRD
END;
PROCEDURE P* (X:T);
END;
ENDM.
На первый взгляд это отступление от РОРИ. Ибо программисту использующему нужен только интерфейс. На самом деле, в к концу 80-началу 90х годов получил распространение приём, что язык пишется как часть системы программирования.
//На 3 этаже стояли перфораторы
Прграммистдимеет делло с программой, которую просматривает в режиме онлайн.
С этой точки зрения интересен Eifel, который генерировал документирующие модули.
В современных ЯП (Си шарп, Джава) интерфейс и реализация не разделяются. На этот шаг пошли потому, что там есть программы, коотрые генерируют документацию по исходным кодам.
Наследование кода – Контрол-Ц – Контрол-В [:]||||||||||||[:]
Программисты не любят писать документацию, но теперь есть средства, которые генерируют её автоматически по комментариям.
В Обероне от Модулы осталось поняте Модуля, исчезли списки определений, но появились списки экспорта, и оставили только один способ импорта – IMPORT. Также отсутствует понятие главной проргаммы. Считается, что пользователь погружён в ОС, и модули экспортируют туда себя. Операционная среда Оберон предоставляет шелл, и пользователь может запускать комманды – экспортированные функции.
После педедыва язык Ада.
//педедыв
Стиль Си: использование префиксов. Что делать если имена конфликтуют имена – сплошной геморрой. Минимум, который необходим – структуризация модулей, хотя бы один уровень.
Язык Ада представдяет собой ультимативную модель.
Есть Пакет – модуль (не путать с джавой)
Пакет состоит из
Спецификации пакета
Тело пакета
package M is
объявления
end M;
package body M is
end M;
Функцию pop на аде написать нельзя, потому что у адской функции не должно быть побочного эффекта.
В Аде есть механизм импорта, но он только для раздельной трансляции.
Импорта как такового нет.
Есть пакет STANDART.
Прространства имён могут вкладываться – коренное отличие от других ЯП.
Спецификация пакетов – неявный список экспорта. Доступны они только потенциально.
Имена пакетов должны быть уникальными.
Явной конструкции импорта нет, ибо в других ЯП они нужны для раздельной трансляции.
Синтаксис: P1.P2. ... .PN
Интересный вопрос: нужна вложенность или нет.
В Аде есть такая штука, как перегрузка имён и функций, что есть в совр ЯП. Но в Аде можно было также перкрывать стандартные процедуры и функции, а акже знаки стандартных операций. Беда в том, что стандартные операции не префиксные (нектоторые неизвестно какие, например a[i]). Если бы вопрос был только о префиксных, то вопрос потенциальной видимости не мешает.
Пусть есть
package Vectors is
type Vector is
function «+»(x,y:Vector) return Vector;
end Vectors;
X, Y,Z:Vectors.Vector;
X:=Y+z – нельзя
vectors.«+»(y,z) – смысл?
//программисты, скрипя сердцем... [:]|||||||||||||||[:]\
Если попытаться на си написать A=B*exp(-i*C)*F(x), где всё комплексное, то математик скажет, что это не язык. Поэтому Страуструп добавил возможность добавления новых операторов.
И возникает вопрос, зачем тогда переопределение операторов если нет непоср области видимости? Поэтому в Аду есть uses список пакетов. Тогда можно для векторов писать Z:=X+Y. На первый взгляд всё замечательно. Но что делать, если возникате конфликт имён? Если X оперделён глобально, в M1, M2. И это приводит к программам, смысл которых от uses меняектся.
Проектирование систем:
top-down
bottom-up
Системы разбиваются на набор модулей, вопрос в том, с каком порядке разбивать.
Сверху вниз: разбить систему на модули, эти модули на ещё модули и т. д. Проблема в том, что делают заглушки. И тут мы тренируемся на лягушатнике, а когда бросают в бассейн – буль-буль
Снизу вверх. Модули нижнего уровня отлаживаются проще. Недостаток: заказчик не знает, что хочет, и меняет заказ, и видит готовую систему в самый последний момент.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Календарь
чт | вт | чт | вт | чт | вт | чт | вт | чт | вт | |
Сентябрь
| 05 | 07 | 12 | 14 | 19 | 21 | 26 | 28 | ||
Октябрь
| 03 | 05 | 10 | 12 | 17 | 19 | 24 | 26 | 31 | |
Ноябрь
| 02 | 14 | 16 | 21 | 23 | 28 | 30 | |||
Декабрь
| 05 | 07 | 12 | 14 |
Материалы к экзамену
Сравнение языков программирования