Языки программирования, 13 лекция (от 17 октября)
Материал из eSyr's wiki.
-
Оператор присваивания
-
Операторы управления
goto
Для выхода более чем из одного уровня вложенности вJava есть goto метка.
Для очистки ресурсов раньше использовали goto очистка_ресурсов, в современных ЯП: try ... finally.
J C# D отличает от C++ референциальная модель объектов. Объект нужно создать. В C++ есть конструкторы и деструкторы, и когда объект перестаёт существовать в памяти, автоматически вызывается деструктор. В С++ захват ресурсов – инициализация. Если нам нужен ресурс, мы обрамляем его в класс Res, в конструкторе которого желается захват ресурса, в деструкторе – освобождение. И при любом выходе из стека будут вызываться деструкторы объектолв, влекущие за собой освобождение ресурсов. В JDC# есть конструкции, которые позволя.т эмульровать эту стратегию.
Пример из UI:
Когда мы проектируем UI, который характеризуется W(indow)I(con)M(ouse)P(ointer). Если пользователь заказал. Чтобы пользователь не дёргался, меняется курсор, более того, когда он видит песочные часы, он ничего не пытается предпринимать, В куче библиотек, например в Си, есть объекты, которые в конструкторе размещают объект, в деструкторе освобождает.
Мы размещаем курсор в стеке, вызывааем долгукю операцию. В чём проблема: даже если не работала такая семантика,
CwaitCursor
showcursor;
longop;
hidecursor;
Если longop завершится аварийно, то курсор так и останется часиками, и пользователи будут считать, что программа зависла.
Goto либо просто не нужно, либо просто вредно.
X.x
goto end;
Y y;
end:
Компилятору очень трудно определить, выполнять свёртку локального объекта Y y, или нет. Компиляторы в этом случае выдают предкпреждение, что свёртка Y y игнорируется.
Программисты Модула-2 говорили, что им не хватает обработки ошибок (исключений).
В Модуле-3 главное понЯтие – Remote Procedure Call.
-
Составной оператор или блок
Чем отиличатеся сост оператор от блока. В Алголе не отличается, в Паскале – да.
Блок – объявление + операторы
Составной оператор – только операторы.
В с++ разница между сост оператором и блоком нивелирована. Там нет разница между операторами и объявлениями. Объявление класса влечет выполнение конструктора.
Как только вохникает разделение объявлений и операторов, при этом понятие составного оператора необязательно. Может быть в языке отдельно блок и отдельно оператор. Такое понятие нуждно чисто синтаксически в языках, где отсутствует явный терминатор.
Ада, М-2, Оберон – понятие сост оператора обсутствует, так как там должно быть ключевое слово для начлаа блока.
Блок – не синтаксическое понятие – он вводит локальные переменные, а потом операторы.
В Паскале есть понятие блока – синтаксически объявления, begin , операторы, end. - это блок паскаля.
Язык Ада -
declare
объявления
begin
операторы
end
В программировании 70х годов счиатлос, что нужно опережающее описание всех переменных. Причём в Аде счиатлось, что нужно сналчала тому, кот читает, знать обо всех объектах, а потом читать, что делается. Смешение считалось дурным тоном.
В Паскале свержу блока может быть либо заголовок процедуры, дибо заголовок главной программы.
В современных ЯП блок эквивалентен составному оператору. (Си++, Джава, Си#). В современных языках беспорядочное объявление операторов является господствующим.
-
Специализированные операторы
Специфичны для конкретных ЯП.
Для языка Ада есть два оператора: select, accept. Относятся к области взаимод параллельных процессов, и их аналогов в другизх ЯП нет или почти нет. Каждый ЯП может ввести некоторые специализ операторы. И при изучении на них надо обращать внимание.
Глава 4. Процедурные абстракции
<коммент>
Ка с тз операторного базиса, так и с тз типового базися ЯП, несильно отличаются друг от друго.
Набор ТД сильно сократился по сравнению со старыми ЯП, набор конструкции тоде. Базисные встроенные объекты с объектами реального мира имеют мало общего, кирпичики, из которого строят модели, очень маленькие и универсальные. Для этого предлагаются некие повторно используемые библиотеки. Например, есть STL, а есть открытые библиотеки типа boost. «то уже более мощные ЯП. В совр ЯП делается брлее мощный упор на средства развития.
</коммент>
Что нужно в языке: язык ассемблера – трудно развиваемый язык, ничего с тз развития нет.
Высказывание одного из разработчиков первых систем обороны: «Эти ребята болтают про ОО, мы это делали в 50х годах то же самое, только называали другими словами».
На защите аспирантов один воскликнул: «Да мы сидели и раньше делали то же самое, и просто не считали это нужным это защищать».
Это неправильный довод, так как тут конти... знания, которые можно передать.
А то, что сделали на асме и джовиале, очень трудно передать.
Знания, написанные на ОО языке, передавать значительно проще.
Можно объектно программированть на асме.
Что нужно в языке для минимальных ср-в развития:
На Фортране 66 года можно было создавать достаточно сложные системы, например компилятор компиляторов. Пользовались двумя вещами – подпрограмма (возниклоа в саом первом варианте), и блок данных (возник тогда же0– ужасно неприятно синтаксич конструкция, но он позволял
BLOCK DATE тимя
объявления данных
END
все объявления должны быть только для какой-то подпрограммы, и они видны только для этой подпрограммы, а тут получается набор глобальных данных, с которым могли взаимодействовать любые подпрограммы. Без этого ничего смерьёзного сделать нельзя.
Стандартная Экспонента была , а продвинутой нет, и её надо было писат, причём по способу использования рони ничем не отличались. Это очень важная парадигма.
Блок данных –изнвачально средство экономии памяти. Потом выяснилось, чо необходим набор глобальных данных для взаимод разных модулей.
Средства развития минимальные есть.
Что нужно, чтобы было развитие: нужен модуль, где набор данных и набор подпрограмм.
Пятая глава – модуль, который есть развитие.
П1. Процедуры и сопрограммы.
Нечто, область кода, которая имеет своё имя, которое можно использовать с помощью вызова. Поэтому в кадлом языке есть понятие процедуры и понятие вызова.
Понятие процедуры соотв понятию мструктурного программирования. Это чёрный ящик, у которого один вход и один выход, а там внутри уже делается что угодно. Но в первых ЯП очевидно было, что процедура – нечто, которое может иметь и несколько входов, и несколько вызодов. Фортран 77 объединял в себе особенности, которые появлись в новых компиляторах. Там кроме SUBROUTINE был ENTRY – это были альтернативные входы, причём альтернативные входы могли иметь альтернативные наборы параметров.
SUBROUTINE P(X, Y)
ENTRY P1(C)
EnTRY P2(C)
напоминает множественное использование goto.
В структурных ЯП такой необходимости просто не возникает.
Кроме того, можно было передавать в качестве параметров метки, и делать потом RETURN 2/
SUBROUTINE P(X, Y, *, *, *)
...
RETURN 2;
ASSIGN 555
GOT M;
Это позволяло окочательно запутаться.
Для того, чтобы совсем запутаться, можно было делать
CALL P(A, B, 333, 25, 555)
И надо было внимательно смотреть на P, и то, не факт, что поймёшь, куда делается переход.
Отдельные шутники предлагали вместо goto сделать come from.
Было ещё одно понятие, коотрое было изгнанно из совр ЯП: сопрограмма.
Процедура: процедура p подразумевает всегда вызов её из надпрограммы, вызывающей, главной. В каждыцй конкретный момент времени вызыв программа становится главной. Причём тут несимметрия: p – точка входа (в ассемблере процедура не отличатеся от метки. Любимая задача лектора: если не call, как его промодулироваать, но ткут ещё надо в стек загрузить адрес возврата. В 360 не было call, зато был такой косвенный опретор перехода). Всегда при выцзове процедуры всегда с первого оператора, а возвращение туда, откуда вызвали, куда скажут. Чёткая несимметрия.
Как обобщить подобную антисимметрию и сделать её симметричной: для эжтого назовём это сопрограммой (coroutinnes) – взаимод программы.
Есть call p2 – управление идёт в точку входа p2, потом делаем call p1, возвращаем туда, откуда вызвали, потом опять делаем call p2, и возвращаем управление туда, откуда ушли. Тут это вместо call называется resume. При resume начинается выполн соотв сопрограммы с точки, где её покинуло.
Книга структурное программирование – три статьи – первая Дейкстры про структурное программиорваиние, вторая Ховарда про типы данных. Третья Уве Дау (один из автор Симулы 67 – основной источник идей для Страуструпа при создании Си с классами), в которой он обсудждал понятие сопрограммы, реализацию в Симуле, возможности применения, насколько оно красивее.
Явный пример: нужно слтить два файла. Не очень приятная задача, нужно гонять циклы то там, то там. Кроме того, возникают лишние циклы, когда один из файлов закончился. А при сопрограммах обе сопрограммы читают из своего файла в результирующиий, вопрос в том, когда передавать управление, и тут всего один цикл. Сигнализация конца файла легко устанавливается и она решаетсмя за один цикл. Чструктура решения более простая, чем при классич решении. Структура сопрограмм похоже на процессы. Это суть квазипараллеьные процессы, и с точки параллельных процессов resume соответствует синхронизации. Для многих задач параллельные процессы естественнее, чем последовательные. Сопрограммы были впервые применены для реализации с языка Кобол. С точки зрения компиляции на что похожа сопрограмма – есть сопрограммы для лексического, синтаксического анализа, генерации кода. Тогда, когда синт анализатору нужна лексема, он вызываает лекс анализатор, и он её отдаёт. Синт анализатор из потока читает лексемы и отдаёт их генератору кода. Компилятор есть сеть взаимод сопрограмм, и эта концепция всем понравилась. Она была внедрена в Симулу 67 и Модулу-2. Для поддержки сопрограмм блыи:
тип COROUTINE
NEW PROCESS(VAR C:COROUTINE, P:PROC; N:INTEGER); - Вирт отвечал, что так получилось чисто исторически, PROC – встроенный тип, процедура без параметров.
Очень похоже на запуск нового потока. Там мы указыаем процедуру без параметров (в виндах можно указываать параметр – код возврата).
Далее была неопределённая вещь – N:INTEGER;
При создании новых потоков наследуется часть контекста. У легковесных нитей и потоков отличаются тем: легковесные общие тем, что у них сегмент данных общий, синхронизация почти не нужна. Счётчик команд различный, код может быть разный, сегмент стека разный – под локальные переменные.
Было две процедуры: TRANSFER(P1, P2), P1, P2 – типа COROUTINE, и потом был RESUME. Р1 передаётся для
COROUTINE – структура
Вирт потом заменил её на ADDRESS – аналог void *
Мы не раньше говорим, что это структура, теперь – что эту структуру помещаем по адресу.
Была ещё процедура IOTRANSFER – вызов процедуры, когда приходило прерывание, там ещё было N – номер прерывание. Это позволяло писать драйверы. Это понятие сопрограммы могло быт полезным, например, при обьработке нажатий клавиш.
В современных ЯП сопрограммы практически не используются.
Основываясь на понятии сопрограммы, можно реализоваьт передачу сигналов. То есть понятие сигналов – квазипараллельного програм, на основе вопрограмм.
Один из главных недостатков сопргр=ограмм – сложность синхронизации оп данным. У каждой сопрограммы свой стек, где хранятся локальные данные. Сопрограммы могут взаимодействовать только через глобальные переменные. Они не могут экспортированть данные наружу.
Кроме того, оно сочтено низкоуровневым.
Понятие потока. RSSUME – оператор языка. Это всё встроено в языка. Это лишний пример того, почему сужается базис – разные вещи могут быть реализованы с разной эффективностью. Теперь есть поток, и его реализация зависит от платформы.
Лектору кажется, что отсутствие понятия сопрограмм связано с наличсием других средств квазипараллеьного программирования реализуемых ср-вами стандартной библиотеки или ОС.
Явным преимущественным Ада – механизм синхронизации процессов (мезанизм рандеву). Единственная новая концепция. Accept – принимал некоторую точку входа от параллеьного процесса. Механизз рандеву всем хорош, но если программировать параллельные процессы на ОС, где пар процессы уже есть, то получалось менее эффективно. И он был полезен, где пар процессов вообще не было. Но там есть ограничение по ресурсам, и там механизм рандеву – стрельбпа из пушки по воробьям.
В джаве есть встроенные средства программирования потоков, кроме того, там есть JNI, чему сопротивлялась Sun, но чего так хотела MS, - вызов нативного кода. Это напрямую противоречит WORA. Но получается так, что реализация встроенного механизма, оказывается менее эфективной, чем использование средств ОС.
Встроенный механизм сопрограмм слижком низкоуровневый, и его нельзя реализовать достаточно универсально на разных архитектурах (ОС). Современные программисты потеряли вкус к параллельному программированияю. Их не нуджно применять для эффективности, их нуджно применять для соотв структуре алгоритма.
Реализация подпрограмм.
П2. Подпрограммы. Передача параметров.
Разные ЯП отличаются с тз реализации отлдичаются механизмом передачи параметров.
Существует семантика in/out и существует способ передачи. В общем случае это разные вещи.
In/out – три класса входных параметров.
Первый класс – in-параметры – передаются толькоих значения
out-параметры – меняют свои значения, но от них не требуется определённости
in/out – требуется как статус определённости, так они и могут получить новые значения.
Это классификация с т.зр. зрения in-out семантики. И способы передачи нужно классифицировать в этих терминах. Способов передачи 6:
-
По значению. Есть факт параметр, есть формальный – и нужно установить взаимод, для in – параметров знач не меняется, для out, in-out – значение может поменяться. При передаче по знач заводится место для параметра, заводится в стеке – для объекта формального параметра. Происходит Копирование из факт в формальный параметр. Установление связи есть фактически копирование. Копирование происходит перед вызовом Происходит загрузка параметров в стек.
-
По результату. Фактический := формальный перед return. Может быть формально и после return. Тут всегда перед CALL происходит загрузка параметров, и её может выполнить главная программа, а вот присвоение можно делать как одна, так и другая, но чаще после return, ибо вызывающая программа знает больше.
-
По значению/результату (по значению результата – чушь собачья, ошибка на экзамене. Изначально ошибка появилась при переписывании лекций с диктофона).
Чем хороши эти способы – отвечают полностью семантике in-out.
Главный недостаток этих способов – копирование, если параметры большие. Понядобились ещё два способа:
-
По адресу/ссылке. Выделяется формальный параметр – адрес фактическое. Место в стеке выделяется под адрес.
-
По имени. Интуитивно понятен.
Когда передаём адрес, мы имеем полный доступ.
В фортране все параметры передавались по ссылке. Преимущество – полный доступ. Опасность – мы не хотим параметр модифицировать. Можно было также передавать константы, и их можно было изменить. Ещё один недостаток – если передаём in-параметр (просто число), то это не эффективно, так как каждый раз используется адрес.
Большинство современных ЯП параметры передаются по значению и по указателю(программист на Си должен моделировать это передачей указателя)-ссылке (появился в СИ++)