лекции (2011) (1160854), страница 3
Текст из файла (страница 3)
Иногда он еще называется блоком.
begin s1; … ; sn end [Паскаль]
{ …………………… } [Си, C++ и другие]
Не во всех языках был реализован составной оператор. Во многих языках(АДА, Модула 2, Оберон) отказались от понятия составного оператора, там группа операторов явно замыкается специальным оператором.
Например:
IF (TRUE)
<OPERATOR1>
<OPERATOR2>
...
<OPERATORN>
END
Условные операторы и многовариантные развилки. Циклы. Оператор
перехода, связанные с им проблемы и способы их решения в современных
ЯП.
IF
С,C++:
if (B) then
Operator1
else
Operator2
АДА:
if B then
S1;S2;S3;...
End if
Оберон:
IF B THEN
S1;S2;S3;S4...
ELSE
S1;S2;S3;S4...
END
Оператор выбора - дискретный случай.
в Паскале:
Case Expr of
список вариантов, Вариант имеет вид const: оператор;
End
В чистом Паскале нет else(default) константы.
В С, C++, Java, C#:
switch (expr) of {
case 1: ... break;
...
case n: ... break;
default: ... break;
}
break - указатель на переход на конец структуры, если его нет, то дальше выполнится следующий case. Если в С++, С, Java - break было писать не обязательно после каждого case, то в С# стало обязательным (ошибка компиляции). Если в С# мы хотим после завершения данного case перейти на следующий надо использовать оператор перехода.
Модула - 2:
CASE expr OF
Список значений1: s11; s12; … ; s1N |
Список значений2: s21; s22; … ; s2N |
….еще списки значений
ELSE ……
END
Ввиду возникающей синтаксической неоднозначности со списком значений вводится спецсимвол – вертикальная черта |
Ада:
Case expr of
when <список констант иди диапазонов> => оператор1
...
when <список констант иди диапазонов> => операторN
when others => операторы
End case;
Операторы циклы.
Выделяют 4 вида цикла:
1. Пока
While B loop .. End loop (ADA)
WHILE B DO .. END (Modula - 2)
While (B) S(C, C++)
While B do S(Pascal)
2. До
REPEAT UNTIL B; (Modula - 2)
do S while (B); (С, C++)
3. FOR
for v:= r1 to t2 do S(Pascal).
for(e1; e2; e3) S; (C++, C)
в Java, C# аналогично С, только там на каждой итерации осуществляется квазистатический контроль, то есть A[i] - должен на самом деле существовать, чтобы не вызвать ошибки.
FOR V:= E1 TO E2[STEP E3(целое значение)] DO END(Модула - 2) - можно было задавать шаг. Он мог быть как отрицательным так и положительным. E1, E2 - типы, к которым применима операция сложения и вычитания.
В 1993 - вышел Оберон - 2, который по идее является минимальным полным языком для написания любой программы, куда вошел и цикл for.
for v in <диапазон> loop .. end loop
for i in A'RANGE loop S:= S + A(i); end loop
for i in A'FIRST..A'LAST loop S:= S + A(i); end loop; (Ада)
4. Бесконечный цикл
LOOP
... IF B THEN ... EXIT
END (Модула - 2)
while (1) {... break...}
for(;;) {... break ...} (C++, C)
Loop
...
when B => exit
...
end loop (Ada)
goto - перейти на помеченное место в программе. В Модуле - 2, Java.
Обероне отсутствует.
Для организации не локальных переходов:
setjmp, longjmp – В Си++ используются для обработки ошибок.
throw, trace – Обработка исключений
Также существуют специальные операторы для организации параллелеризма
Lock(obj) {блок} – Си#. Поток управления блокируется, если блок кем-то используется
accept, select – Ада
Особенности реализации циклов-итераторов в современных ЯП.
Очень полезен. Когда мы ищем максимальный и минимальный элемент контейнера, например.
Представим себе массив:
int[ ] a = new int[N];
for(int i=0; i<a.Length; i++)
{
Что-то делаем с a[i]………
}.
Не лучший способ записи. Результат может зависеть от порядка просмотра.
a[i] – это всегда некоторое вычисление. Что есть в C#, а в С не будет никогда? Конечно, проверка выхода за границу массива.
foreach(double v in a)
{
.............
};//управляющая переменная v просто последовательно принимает значение элементов массива.
Так как квазистатический контроль на каждой итерации цикла считается неэффективным в С# был придуман еще один вариант цикла for - особая форма цикла.
foreach (T o in C) S;
int a[];
foreach (int x in a) S = S + x;
C - произвольная коллекция.
тип T должен наследоваться от IEnumerable, в которой входит такой метод, как получить следующий элемент. В Java сей цикл был реализован в 2005 году.
В общем случае контейнер языка C# - это класс, поддерживающий интерфейс IEnumerable. И компилятор будет обрабатывать конструкции подобных классов специфическим образом.
GetEnumerator(); //выдает iterator, нужный для пробегания по элементам, а не по индексам.
Также у класса поддерживающего интерфейс IEnumerable, есть свойства Current и метод MoveNext()
Создатели языка Java сделали еще метод hasNext().
Java
используется для поэлементного просмотра коллекций (цикл for-each). Она имеет вид;
for (T v:Coll)S
Здесь Coll — коллекция элементов (типа T или приводимых к типу T).
Переменная v на каждой итерации цикла принимает значение очередного элемента коллекции.
Для того, чтобы объекты класса-коллекции могли появляться в цикле for-each, класс должен реализовать интерфейс Iterable
4. Процедурные абстракции
Подпрограммы и сопрограммы. Операторы возврата и возобновления.
Подпрограммы
Почему «подпрограмма?» Управление входит в подпрограмму только через имя блока. И возвращается туда, где была вызвана подпрограмма, и ни в какую другую точку вернуться не может.
CALLER – вызывающий подпрограмму(надпрограмма)
CALLEE – вызываемая подпрограмма
Сопрограммы
Впервые механизм сопрограмм был придуман для компилятора COBOL.
При вызове сопрограммы управление передается в точку, непосредственно следующую за местом, где оно покинуло сопрограмму.
Сопрограммы – фактически квазипараллельные процессы.
Модула-2
Вызов сопрограммы аналогичен длинному переходу на некоторый абстрактный адрес, по которому находится команда сопрограммы, с которой нужно начать выполнение. Но, помимо этого, нужно ещё как-то запомнить адрес возврата и другую служебную информацию, передать входные параметры, наследуется часть контекста. Для этой цели Вирт в своём языке Модула-2 ввёл тип данных ADDRESS (то же самое, что и
«void *»).
Этот тип данных является потенциальной дырой в системе безопасности, т.к. любой указатель «Т *» автоматически приводится к «void *», и возможно обратное явное преобразование «Т *» = (Т *)«void *».
Итак, в Модуле-2 вызов сопрограммы имеет такой вид:
NEWPROCESS(P, C,N);
Где P – процедура без параметров типа PROCEDURE, который является встроенным, C – переменная типа ADDRESS. N - размер области для «запоминания» информации. Область начинается с адреса C
PROCEDURE NEWPROCESS(P : PROCEDURE; VAR C : ADDRESS; N : INTEGER);
Передача управления от одного процесса другому на уровне сопpогpамм осуществляется процедурой "Передать управление от процесса P1 процессу P2". В Модуле-2 эта процедура выглядела как
PROCEDURE TRANSFER(VAR P1,P2 : ADDRESS);
При этом в переменную P1 записывается запись реактивации этого процесса, а значение переменной P2 определяет запись активации процесса P2.
RESUME; – оператор языка.
В современных языках сопрограммы трансформировались в понятие потока.
.Net Thread Квазипараллельный поток
C# 2.0:
foreach(T x in C)
Тип T должен реализовывать интерфейс IEnumerable. Этот интерфейс содержит метод GetEnumerator(), который возвращает объект некоторого типа, который должен реализовывать интерфейс IEnumerator со свойствами Current, методом Reset и методом bool MoveNext(). Любой класс, поддерживающий интерфейс IEnumerable должен содержать класс, поддерживающий IEnumerator.
yield-операторы в C# 2.0:
yield return <expression>;
yield break;
Итератор – процесс(сопрограмма), выдающий последовательно очередные элементы коллекции тогда, когда они понадобятся. yield-оператор используется в блоке итератора для предоставления значения объекта перечислителя или для сообщения о конце итерации. Т.е. это не простой «return» или «break», а оператор, совмещающий в себе дополнительно работу по переходу между сопрограммами (от процесса-итератора в
основной процесс). Выражение expression вычисляется и возвращается в виде значения объекту перечислителя; выражение expression должно неявно преобразовываться в тип результата итератора.
Public class List
{
//using System.Collections;
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:
foreach (int i in Power(2, 8))
{
Console.Write("{0} ", i);
}
}
}
/* Output:
2 4 8 16 32 64 128 256 */
С точки зрения современных языков программирования сопрограммы оказались глубоко зарыты в потоках и в итераторах. Явно остались лишь те подпрограммы, у которых есть 1 вход и 1 выход.
Процедуры и функции в современных ЯП.
Передача параметров: семантика и способы реализации.
-
Через глобальные данные
-
Через параметры
Виды формальных параметров(семантика):
• Входные (in) – должны быть определены до входа
• Выходные (out) – должны быть определены к моменту выхода
• Вх/Вых(InOut) – и то и другое
Способ передачи – способ связывания фактических и формальных параметров:
• По значению (семантика - in) (При вызове подпрограммы фактические параметры
копируются в Запись Активацаии)
• По результату (семантика – out) (При выходе из подпрограммы из записи активации
формальные параметры копируются в фактические)
• По значению/результату (семантика – InOut) (При вызове подпрограммы фактические параметры копируются в Запись Активацаии, При выходе из подпрограммы из записи активации формальные параметры обратно копируются в фактические)
• По адресу(по ссылке) (семантика - любая) (При передаче по Адресу в Запись активации
копируется адрес фактического параметры. Именование и разыменование происходят
автоматически )
• По имени
В Pascal 2 способа передачи параметров – по ссылке и по значению.
Если у параметра присутствует Var, то объект передается по адресу. А если не присутствует, то по значению.
Ада83:
Квалификаторы: in, out, inout
Procedure P(int X:T;inout Y:T;out Z:T);
X может быть выражением типа T. Компилятор может вставлять квазистатические проверки. Эффект процедуры – модификация Y и Z. Каков способ передачи определяет компилятор (что не есть хорошо, т.к. различные компиляторы в одной и той же ситуации могут выбрать разные способы передачи, что приведёт к различной работе программ).
Запись активации
…..
……
Место для формальных параметров
Энтропия – явление, при котором программа может выдавать различные результаты при одних и тех же исходных данных. Если в программе есть энтропия, то это очень плохо. Очевидно, что при программировании на Ада риск энтропии значительно повышается, т.к. не известно какой способ передачи выберет в этот раз компилятор