Лекция 8 (Лекции (2009) (Саша Федорова))
Описание файла
Файл "Лекция 8" внутри архива находится в папке "Лекции (2009) (Саша Федорова)". Документ из архива "Лекции (2009) (Саша Федорова)", который расположен в категории "". Всё это находится в предмете "языки программирования" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "Лекция 8"
Текст из документа "Лекция 8"
11
Лекция 8.
Операторы перехода
Должно обсудить break, continue и return, которые также являются операторами передачи управления. Все они – заменители оператора goto.
В M-2, Java- goto отсутствует в принципе.
Замечание. В некоторых языках программирования goto – основной оператор.В современны языках goto если и присутствует, то играет крайне ограниченную роль и является локальным(то есть может использоваться только в блоке, кроче: областью действия метки является некий блок, ограниченный записью активации текущей процедуры или функции.)
В С/С++ есть операторы setjmp longjmp(низкоуровненвые) – используются для обработки ошибок, если ее трудно обработат локальным образом. В современныхязыках прграммирования для таких дел предусмотрен механизм исключений. (Еще один набор операторов – throw и raise – мы рассмотрим чуть позже. )
Остались нам специфические операторы, связанные с реализацией такого критического момента, как распараллеливание.
C# -lock(obj) (objблок) означает, что поток управления, входящий в этот блок, в любой момент времени только один. Все остальные потоки блокируются до той поры, когда поток, захвативший этот блок, не выйдет из захваченного блока.
С другой стороны, существует другая семантика оператора lock.
Ада
Accept
Select
Данные функции позволяют параллельное выполнение процессов.
Замечание.Какие-то спецоператоры для распараллеливания будут в других курсах.
Мы рассмотрели базис традиционных процедурных языков, и увидели, что все они недалеко ушли друг от друга. И набор типов данных, и набор операций очень похожи. Отличия только в «деталях»: языки программирования различны с точки зрения средств развития и некоторых абстракций. Что есть минимальное средство развития?
В Фортране, например, одно из средств развития – понятия подпрограммы. Средствами данного языка вполне можно реализовать вычисление интеграла и добавить его в средства развития.На самом нулевом уровне в Фортране есть модульности(каждая функция – в отдельном модуле). Сердито и неудобно, но есть. Заметим, что яззыка паскаль, по уровню более высокий, чем Фортран, не обладает этими средствами. Именно поэтому в него первм делом стали добавлять модульность и межмодульные связи. Также с языковой точки зрения были добавлены средства на обработку массивов.
var a: T;
b absolute a; //означает, что b и a начинаюются с одного адреса.
-
Очевидно, можно адресовать элементы массива любой длины
-
И можно писать вообще что угодно!
Но зато отсутствует надежный контроль.
В С, C++, Java, C# - средства развития лучше, они намного лучше «защищены», чем Фортран или Паскаль. В них развиты средства создания новых абстракций.
Глава 5. Подпрограммы
Пункт 5.1 Потоки управления: подпрограммы и сопрограммы
Рассмотрим следующие потоки:
-
Потоки управления
-
Потоки данных
В современных языках программирования подпрограммы удовлетвоярют правилам структурного программирования, то ест являются блоками(конструкциями с одним входом и с одним выходом)
Но в PL1 это правило не выполнялось и все эффективно путалось
ENTRY имя (.........)
ENTRY – точка входа. У процедуры могло быть несколько точек входа.
Подпрограмма могла иметь параметры-метки:
SUBROUTINE FOO(…*, *, *)
RETURN номер *-ки
.//число меток тут 3, значит номер мог быть 1, 2, 3.
Вызов:
CALL FOO(10; 20; 30); //невозможно понять, куда мы прыгнули. И понять, как работает программа тоже невозможно.
Для ранних языков программирования характерно богатство управляемых конструкций. В современных – напротив, только ветвления и циклы, в лучшем случае – управлляемые циклы.
Почему «подпрограмма?» Управление входит в подпрограмму только через имя блока. И возвращается туда, где была вызвана подпрограмма, и ни в какую другую точку вернуться не может.
Исключение – аварийная ситуация, когда априорный порядок выполнения нарушается.
CALLER – тот, кто вызывает,
CALLEE – вызываемая подпрограмма.
Естесттвенным образом, образуются вложенные области выполнения.
На первый взгляд, необходимо реализовать операцию выполнениявызова и возврата. Однако такой подход в общем случае не универсален.
Сначала было SUBROUTINE.
В начале 60-х годов появилась COROUTINE – сопрограмма.
Рассмотрим различные виды подпрограмм.
Легко заметить неравноправность между процедурой и основной программой: возвращаемся мы всегда в точку вызова, а процедура вызывается всегда «с самого начала».
Равенство возникнет тогда, когда вызываться подпрограмма будет с того места, откуда она первый раз вернулась.. Управление подобно игре в мяч:передал – стой на месте. Вот так:
При работе с Cobol было замечено, что компилятор осуществляет некоторую определенную последовательность действий : однопроходные трансляторы. Любой компилятор на Cobol можно представить как совокупность независимых взаимодействующих подпрограмм, передающих друг другу управление. Это бло похоже на взаимодействие отдельных программ.
В Модула-2 появился аналог COROUTINE, называемый ADDRESS –аналог void * в Си.
Напомним, что к void * можно неявно привести любой указательный тип. Наоборот – явно:
Void * T *//неявно
T * void *//только явно
Программисты Модула-2, что странно, часто использовали тип данных ADDRESS.
Любой объект данных принадлежит некоторому единственному типу данных Т. Все типы данных распадаются на непересекающиеся класссы эквивалентности, раззличающиеся по множеству операций и структуре. Классификация языков на строгие и нестрогие определяется по тому, каковы его ограничения на типизацию.
Moдула-2: 2 оператора, посвященных распараллеливанию процессов
-
NEWPROCESS – организация квазипараллельных процессоров. В современых языках программирования сегмент данных общий, но потоки управления могут быть различные.
NEWPROCESS(P, C, N);
NEWPROCESS(P: PROC, VAR C:ADDRESS, N: INTEGER)
P-процесс, процедура
C-аддрес процесса или процедуры
N-длина области, отводдящейся под работу данному процессу.
-
PROCEDURE TRANSFORM(VAR C1, C2: ADDRESS)
Оператор осуществляет передачу управления от С1 к С2.
Подпрограмма P вызов P формальные параметры размещаются в стеке или некотрой глобальной области данных, доступной для процедуры(в отличие от случая со стеком, тут невозможна рекурсия).
Если мы не будем сохранять значения в стеке, то рекурсия возможна.
Для работы параллельных процессов необходима запись активации.
Содержит:
-
Адрес возврата
-
Значения формальных параметров
-
Значения локальных переменных
Где она находится? Это дело самого программиста.
Что такое N в параметре оператора NEWPROCESS? Оцениваем сттек, размеры локальных параметров и т. д.
Получаем противоречие: язык Модула-2 высокого уровня, а объяснить, чо такое N можно только на уровне ассемблера, что ввообще характерно для общего уровня реализации сопрограмм.
Понятие спрограммы, как мы видим, достаточно мощное, однако в современные языки программирования не вошло. Почему?
Сопрограммы – это разновидность квазистатических процессов, а в этом случае лучше использовать потоки.
Пример: Java – понятие JVM(java Virtual Mashine), а в ней – понятие потока. В .Net есть понятие потока Thread – из WinApi, а в самой .NET – NativeThread. Но это – квазистатичные потоки. Понятие сопрограммы заменилось на квазистатический поток. Они реализованы точно так же, как и сопрограммы, но на уровн компилятора, а не на уровне библиотеки.
Каждая сопрограмма всегда похожа на процедуру: по потоку мы всегда указываем, какую процедуру будет использовать наш поток.
Уже в C# 2.0 появилось несколько довольно любопытных понятий:
Набор FOREACH для пробега по коллекции. А коллекция это любой интерфейс, представляющий IEnumerable – интерфейс, состощий из двух методов:
GetEnumerator();
Current(); //ссылка на текущий элемент
bool MoveNext(); //удалось или нет перейти на следующий элемент.
Reset();
Замечание
В начале для просмотра первого элемента можно сделат MoveNext.
Если писать свои классы, то надо будет дополнительно сделать свой Enumerator. Проще всего, конечно, сделать «наследование»(ctrl+c, ctrl+v). Но если наследование кода проще, чем наследование классов, это не очень хорошо.
Развитие C# шло в интерактивном направлении: появились yield-операторы. yield-операторы могут встречаться в двух видах:
-
yield return obj;
-
yield break;
Метод getEnumerator возвращает информацию о текущей итерации. Представим, что процесс итерации записан отдельно и наша программа по очереди выдает каждый элемент коллекции.
Идея: запрограммировать сопрограмму, которая по очереди выдает каждый элемент коллекции. Написать такой цкил – очень просто.
В C# своя коллекция – чаще всего она основана на коллекции из .NET.
class MyCollection: IEnumerable{
public IEnumerator GetEnumerator(){
сопрограмма
yield break; //означает, что соответствующая сопрограмма закончилась
}
..................................
};
Система C# по IEnumeratorn генерирует встроенный объект. Просходит вызов. MoveNext – пока есть трансфер управления на следующий элемент внутри сопрограммы до следующего yield break
Практически мы этот цикл разбиваем на куски.
Представление итератора как некоторого отдельного процесса очень удобно и логично – это взялось из функционального программирования, где существуют бесконечные структуры данных (lazy computation).
Замечание.Понятие итератора и сопрограммы очень похожи. Итератор выдает ссылки на элемент колекции тогда, когд понадобится. Почему в C# итератор вдруг реализован именно таким образом? Дело в том, что туда отдельно начали добавлять элементы функционального программирования.
В C# 2.0 пвявились анонимные делегаты, а в C# 3.0 – лямбда-функции. С точки зрения современных языков программирования сопрограммы оказались глубоко зарыты в потоках и в итераторах. Явно остались лишь те подпрограммы, у которых есть 1 вход и 1 выход.
Пункт 5.2 Поток данных в подпрограммах(передача данных)
Передача данных в подпрограммах может осуществлляться
-
через глобальные данные
-
через параметры
Механизм глобальных переменных вреден тем, что в нем не работает защита абстракции, сддедовательно, возникает побочный эффект. Побочный эффект можно определеить двояко.
-
Как действие процедуры или функции, в результате которого она меняет значение внешних переменных
-
Как модификация процедурой или функций тех данных, которые являются глобальными относительно других данных
Желательно, чтобы побочный эффект при написании процедуры происходил относительно ее параметров, а не относительно глобальных переменных.
Ведь если мы допустим такое, может выясниться, что несколько процедур используют одну и ту же глобальную переменную, меняя при этом свое значение, а это часто приводи к неразберихе.
Отсюда в современных языках возобладало мнение, что данные лучше передавать только через параметры.
В объектно-ориентированном подходе проблема глобальных переменных исчезла, и он был очень поззитивно вооспринят
Пусть, к примеру, в нашем классе была функция-член void f() {……i……..}
Тогда если i – член класса, виден только он и боьше никто.
Сосредоточимся на передаче данных через параметры.