Языки программирования, 16 лекция (от 26 октября)

Материал из ESyr's Wiki pages.

Перейти к: навигация, поиск

Языки программирования 26.10.06


В прошлый раз мы остановились на межмодульных связях.


Библиотечный модуль Модулы дыва состоит из двух подмодулей, связанных одним именем.:

  1. Модуль-интерфейс, гнде только определения

  2. Модуль-реализация. Реализация и дополн объявления для неё, а так же инициализирующая часть.

Дельфи:

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


Не разрешаются вложенные моудли.


Возникают два понятия: потенциальная и непоср видимость.


Непосредственна – конгда имя можно употреблять без уточнЯюзей информации

Протенцияалдьная – с уточняющей, указание имени модуля через точку. Везде через точку, только С++ выпендрился, там ::


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


Все имена равноправны.


Кроме понятие экспорта, есть понятие импорта. Импорт может быть как в модуль определений, так и вмоудль реализации. Таблицпа имён из модуля реализаций – конкатенация таблицы из модуля поерделений.


Модуль реализации только импортироет, как из модуля определений (всегда, неявно), так и из других модулей.


Есть две формы импорта:

IMPORT список модулей.

Стандартные и пользовательские модули абсолютно равноправны, при этом реальный ждоступ получаем тогда, когда делуем предожение IMPORT. Оно должно быьб первым среди всех объявлений. С точки зрения реализации компилятор должен просмотреть все папки поиска, и подкачать соотв таблицы имён. При этом соотв имена в этом модуле, если в нём есть предложение IMPORT, все имена из него ст ановятся видимы потенциально. Приходиться писать InOut.WriteLn. Программистов это раздражает, поэтому есть другая формаи импорта: FROM имя модуля IMPORT список идентификаторов – и имена становятся видимы непосредственно. Это сделано в дань программистом, которые ленятся набивать длинные имена. Беда непоср импорта в конфликте имён. Причём могут конфликтовать как с именами других модулей, так и с локальными. Локальные имеют больший приоритет (граждане первого сорта имеют больше прав, относительно иммигрантов). Это достаточно удобная модель.


Отличия Дельфи:

Имена из модульной реализации недоступны никому, но есть едиственная форма предложения импорта: uses список модулей – импорт непосредственный. Если имена конфликтуют между собой, то в этом случае непоср видимост снимается.


С первой точки зрения этот подход более улдобен, но когда лектор столкнулся с программированием в Дельфи и Модуле-2, то на Модуле-2 текст читать приятнее. Ибо когда в uses полтора десятка модулей, то дрогадаться, из какого модуля идентификатор, трудно. Поэтому Борланд всегда поставлял ос всеми своими компиляторами grep. А в Модуле-2 мы обречены на успех при поиске по файлу.


Порядок загрузки модулей поерделяется порядком зависимостей. В момент загрузки доступна инициализация – begin ... end. .


В этих языках есть принцип – РОРИ, Разделение

  1. Определения

  2. Реализация

  3. Использования

Главенствовал в ЯП в 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. Также отсутствует понятие главной проргаммы. Считается, что пользователь погружён в ОС, и модули экспортируют туда себя. Операционная среда Оберон предоставляет шелл, и пользователь может запускать комманды – экспортированные функции.


После педедыва язык Ада.

//педедыв

Стиль Си: использование префиксов. Что делать если имена конфликтуют имена – сплошной геморрой. Минимум, который необходим – структуризация модулей, хотя бы один уровень.


Язык Ада представдяет собой ультимативную модель.

Есть Пакет – модуль (не путать с джавой)

Пакет состоит из

  1. Спецификации пакета

  2. Тело пакета

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

Системы разбиваются на набор модулей, вопрос в том, с каком порядке разбивать.

  1. Сверху вниз: разбить систему на модули, эти модули на ещё модули и т. д. Проблема в том, что делают заглушки. И тут мы тренируемся на лягушатнике, а когда бросают в бассейн – буль-буль

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

Личные инструменты