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

Материал из eSyr's wiki.

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

Предыдущая лекция | Следующая лекция

  1. Оператор присваивания

  2. Операторы управления

Содержание

[править] Часть 1. Основные понятия традиционных процедурных ЯП

[править] Глава 3. Операторный базис ЯП

[править] Пункт 2. Ветвление.

[править] 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 (RPC).


[править] Пункт 3. Составной оператор или блок.

Чем отличается сост оператор от блока. В Алголе не отличается, в Паскале – да.


Блок – объявление + операторы

Составной оператор – только операторы.

В с++ разница между сост оператором и блоком нивелирована. Там нет разница между операторами и объявлениями. Объявление класса влечет выполнение конструктора.


Как только возникает разделение объявлений и операторов, при этом понятие составного оператора необязательно. Может быть в языке отдельно блок и отдельно оператор. Такое понятие нужно чисто синтаксически в языках, где отсутствует явный терминатор.

Ада, М-2, Оберон – понятие сост оператора отсутствует, так как там должно быть ключевое слово для начала блока.


Блок – не синтаксическое понятие – он вводит локальные переменные, а потом операторы.


В Паскале есть понятие блока – синтаксически объявления, begin , операторы, end. - это блок паскаля.


Язык Ада -

declare

объявления

begin

операторы

end


В программировании 70х годов считалось, что нужно опережающее описание всех переменных. Причём в Аде считалось, что нужно сначала тому, кто читает, знать обо всех объектах, а потом читать, что делается. Смешение считалось дурным тоном.


В Паскале сверху блока может быть либо заголовок процедуры, либо заголовок главной программы.


В современных ЯП блок эквивалентен составному оператору. (Си++, Java, Си#). В современных языках беспорядочное объявление операторов является господствующим.


[править] Пункт 4. Специализированные операторы.

Специфичны для конкретных ЯП.


Для языка Ада есть два оператора: select, accept. Относятся к области взаимодействия параллельных процессов, и их аналогов в других ЯП нет или почти нет. Каждый ЯП может ввести некоторые специализированные операторы. И при изучении на них надо обращать внимание.


[править] Глава 4. Процедурные абстракции.


<коммент>

Ка с тз операторного базиса, так и с тз типового базиса ЯП, несильно отличаются друг от друго.


Набор ТД сильно сократился по сравнению со старыми ЯП, набор конструкции тоже. Базисные встроенные объекты с объектами реального мира имеют мало общего, кирпичики, из которого строят модели, очень маленькие и универсальные. Для этого предлагаются некие повторно используемые библиотеки. Например, есть STL, а есть открытые библиотеки типа boost. «то уже более мощные ЯП. В совр ЯП делается брлее мощный упор на средства развития.


</коммент>


Что нужно в языке: язык ассемблера – трудно развиваемый язык, ничего с т.зр. развития нет.


Высказывание одного из разработчиков первых систем обороны: «Эти ребята болтают про ОО, мы это делали в 50х годах то же самое, только называали другими словами».


На защите аспирантов один воскликнул: «Да мы сидели и раньше делали то же самое, и просто не считали это нужным это защищать».


Это неправильный довод, так как тут конти... знания, которые можно передать.


А то, что сделали на ASMе и джовиале, очень трудно передать.


Знания, написанные на ОО языке, передавать значительно проще.


Можно объектно программировать на ASMе.


Что нужно в языке для минимальных ср-в развития:

На Фортране 66 года можно было создавать достаточно сложные системы, например компилятор компиляторов. Пользовались двумя вещами – подпрограмма (возникла в самом первом варианте), и блок данных (возник тогда же – ужасно неприятно синтаксическая конструкция, но он позволял

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, зато был такой косвенный оператор перехода). Всегда при вызове процедуры всегда с первого оператора, а возвращение туда, откуда вызвали, куда скажут. Чёткая асимметрия.


Как обобщить подобную антисимметрию и сделать её симметричной: для этого назовём это сопрограммой (coroutine) – взаимодействующие программы.

Есть call p2 – управление идёт в точку входа p2, потом делаем call p1, возвращаем туда, откуда вызвали, потом опять делаем call p2, и возвращаем управление туда, откуда ушли. Тут это вместо call называется resume. При resume начинается выполнение соответствующей сопрограммы с точки, где её покинуло.


Книга структурное программирование – три статьи – первая Дейкстры про структурное программиорваиние, вторая Ховарда про типы данных. Третья Уве Дау (один из автор Симулы 67 – основной источник идей для Страуструпа при создании Си с классами), в которой он обсуждал понятие сопрограммы, реализацию в Симуле, возможности применения, насколько оно красивее.

Явный пример: нужно слить два файла. Не очень приятная задача, нужно гонять циклы то там, то там. Кроме того, возникают лишние циклы, когда один из файлов закончился. А при сопрограммах обе сопрограммы читают из своего файла в результирующий, вопрос в том, когда передавать управление, и тут всего один цикл. Сигнализация конца файла легко устанавливается и она решается за один цикл. Структура решения более простая, чем при классическом решении. Структура сопрограмм похоже на процессы. Это суть квазипараллельные процессы, и с точки параллельных процессов resume соответствует синхронизации. Для многих задач параллельные процессы естественнее, чем последовательные. Сопрограммы были впервые применены для реализации с языка Cobol. С точки зрения компиляции на что похожа сопрограмма – есть сопрограммы для лексического, синтаксического анализа, генерации кода. Тогда, когда синтаксический анализатору нужна лексема, он вызывает лексический анализатор, и он её отдаёт. Синтаксический анализатор из потока читает лексемы и отдаёт их генератору кода. Компилятор есть сеть взаимодействующих сопрограмм, и эта концепция всем понравилась. Она была внедрена в Симулу 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 – принимал некоторую точку входа от параллельного процесса. Механизм рандеву всем хорош, но если программировать параллельные процессы на ОС, где пар процессы уже есть, то получалось менее эффективно. И он был полезен, где пар процессов вообще не было. Но там есть ограничение по ресурсам, и там механизм рандеву – стрельба из пушки по воробьям.


В Java есть встроенные средства программирования потоков, кроме того, там есть JNI, чему сопротивлялась Sun, но чего так хотела MS, - вызов нативного кода. Это напрямую противоречит WORA. Но получается так, что реализация встроенного механизма, оказывается менее эфективной, чем использование средств ОС.

Встроенный механизм сопрограмм слишком низкоуровневый, и его нельзя реализовать достаточно универсально на разных архитектурах (ОС). Современные программисты потеряли вкус к параллельному программированию. Их не нужно применять для эффективности, их нужно применять для соотв структуре алгоритма.


Реализация подпрограмм.

[править] Пункт 2. Подпрограммы. Передача параметров.

Разные ЯП отличаются с т.зр. реализации отличаются механизмом передачи параметров.


Существует семантика in/out и существует способ передачи. В общем случае это разные вещи.


In/out – три класса входных параметров.

Первый класс – in-параметры – передаются только их значения

out-параметры – меняют свои значения, но от них не требуется определённости

in/out – требуется как статус определённости, так они и могут получить новые значения.

Это классификация с т.зр. зрения in-out семантики. И способы передачи нужно классифицировать в этих терминах. Способов передачи 6:

  1. По значению. Есть факт параметр, есть формальный – и нужно установить взаимод, для in – параметров знач не меняется, для out, in-out – значение может поменяться. При передаче по знач заводится место для параметра, заводится в стеке – для объекта формального параметра. Происходит Копирование из факт в формальный параметр. Установление связи есть фактически копирование. Копирование происходит перед вызовом Происходит загрузка параметров в стек.

  2. По результату. Фактический := формальный перед return. Может быть формально и после return. Тут всегда перед CALL происходит загрузка параметров, и её может выполнить главная программа, а вот присвоение можно делать как одна, так и другая, но чаще после return, ибо вызывающая программа знает больше.

  3. По значению/результату (по значению результата – чушь собачья, ошибка на экзамене. Изначально ошибка появилась при переписывании лекций с диктофона).

Чем хороши эти способы – отвечают полностью семантике in-out.

Главный недостаток этих способов – копирование, если параметры большие. Понадобились ещё два способа:

  1. По адресу/ссылке. Выделяется формальный параметр – адрес фактическое. Место в стеке выделяется под адрес.

  2. По имени. Интуитивно понятен.

Когда передаём адрес, мы имеем полный доступ.

В фортране все параметры передавались по ссылке. Преимущество – полный доступ. Опасность – мы не хотим параметр модифицировать. Можно было также передавать константы, и их можно было изменить. Ещё один недостаток – если передаём in-параметр (просто число), то это не эффективно, так как каждый раз используется адрес.


Большинство современных ЯП параметры передаются по значению и по указателю(программист на Си должен моделировать это передачей указателя)-ссылке (появился в СИ++)


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


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

Материалы к экзамену
Сравнение языков программирования


Эта статья является конспектом лекции.

Эта статья ещё не вычитана. Пожалуйста, вычитайте её и исправьте ошибки, если они есть.
Личные инструменты
Разделы