PROGLANG (1129115), страница 2
Текст из файла (страница 2)
for
В 1974 году Кнут тоже в своей статье написал о goto и структурном программировании, причем он утверждал, что оператор goto можно использовать в структурном программировании, не нарушая семантики структурного значения, для обработки исключительных ситуаций.
В традиционных языках мы встречали попытки модифицировать обычный оператор goto: break, continue, return. А профессор Вирт оказался круче – он попросту вообще отказался от оператора goto в языках Модула-2 и Оберон.
Процедурные абстракции (подпрограммы)
программа подпрограмма программа Р сопрограмма Р1
RESUME P1
CALL
RESUME P
RETURN RESUME P1
традиционная процедурная абстракция параллельная сопрограмма на COBOL
В Модуле-2 для обеспечения сопрограммной работы существует стандартная функция
TRANSFER (VAR P1, P2 : ADDRESS);
где P1 и P2 – контексты процессов. Но чтобы создать контексты нужно использовать функцию
NEWPROCESS (P : PROCEDURE; VAR K : ADDRESS; N : CARDINAL);
При вызове процедур используют следующие способы передачи параметров:
-
по значению;
-
по результату;
-
по значению (результату);
-
по ссылке (адресу);
-
по имени (придает гибкость Алголу-60, но совершенно не эффективен)
Глава IV Статическая параметризация
В следующих языках для статической параметризации используют:
Ada – родовые сегменты;
С++ – шаблоны (типы и функции).
Рассмотрим родовые пакеты языка Ada, если у нас есть пакет Stack
package Stacks is
type Stack is record
body : array [1..100] of integer;
top : integer := 1;
end record;
... процедуры
end Stacks
то поменять тип стека параметрически нельзя. Раньше (в С) использовали void * для хранения данных различных типов. Использовать макропроцессор для параметризации типов не разумно из соображений эффективности. Поэтому в Ada сделали некоторые допущения:
generic
<параметры>
<пакет> (спецификация)
...
<тело_пакета>
Эта спецификация может быть определена и в отдельном модуле. Перепишем тело нашего пакета стеков
generic
type T is private;
StackSize : integer := 100;
package GStack is
type Stack is record
body : array [1..100] of T;
top : integer := StackSize;
end record;
... процедуры
end GStack
тогда работать с ним нужно так
with GStack; use GStack;
package P is
...
package IntStacks is new GStack (integer, 128);
...
Таким образом мы объявили новый пакет – целые стеки, а переменная
v : IntStacks.GStack
переменная типа IntStack.
Напишем пример скалярного умножения массива
generic
type T is digits <> точность еще не определена
function GScal (A, B : array (range <>) of T);
но так нельзя, так как тип массива анонимный и фактический параметр не формализуются, поэтому надо
generic
type T is digits <>
type ArrT is array (range <>) of T;
function GScal (A, B : ArrT) return T;
...
type Float_Arr is array (range <>) of float;
function FScal is new FScal (float, FloatArr);
...
Хотели один параметр – получили два!? Попытаемся еще обобщить для целых типов, булевских или других (Т – должен быть произвольным).
generic
type T is private;
type ArrT is array (range <>) T;
with function «+»(A, B : T) return T is (<>);
with function «*»(A, B : T) return T is (<>);
with GScal (A, B : ArrT) return T;
...
function FloatScal () is new GScal (float, FloatArr, «+», «*»);
компилятор из контекста догадается, что + и * – вещественные, так как мы указали is (<>).
Перечислим типы подходящие в качестве формальных
1. Общие
type T is private;
type T is limited private; (если не надо производить операции + или *)
2. Вещественные type T is digits <>;
3. Регулярные type ArrT is array (range<>) of T;
4. Дискретные type DT is range <> of T;
5. Фиксированные type FT is delta T is range <>;
6. Параметр-процедура with function ... return ... [is(<>)]
7. Параметр-переменная имя тип;
Единственный способ передавать параметры-функции – это использовать родовую функцию, например, в задаче интегрирования разных функций.
Критические потребности подразумевают однородность родовых сегментов вплоть до раздельной компиляции (следствие: независимость компиляции определения от конкретизации), а также надежность (квазистатический контроль).
Шаблоны придают программированию удобство, генерация будет зависеть от контекста конкретизации (естественно усложняется разработка компилятора). Страуструп начал работу над шаблонами в 1986 году, стандартная библиотека шаблонов появилась только через 10 лет, экспериментальная система появилась в 1990 году.
Функция
template <список_типовых_параметров> прототип_функции;
Например
template <class T> void Swap(T& a, T& b);
Скомпилировать полностью в данном случае нельзя, так как ничего не известно о типе Т. Поэтому проводят проверку статическую и динамическую, единственное требование – хотя бы один из параметров должен быть типа, указанного в списке типовых параметров. Все конкретизации шаблона имеют то же имя, компилятор различает из только по профилю параметров: swap (i,j); или swap <int> (i,j);.
С неявными преобразованиями вообще тяжело для полнопрофильных конкретизаций:
int i; char c;
swap (i, c); так не пойдет, а надо: swap <int> (i, c);
Так слово explicit перед конструктором X(int) указывает, что это не конструктор преобразования, а просто конструктор с параметром типа int. Проблемы возникают также и на курорте, если тело шаблона описано в подключаемом файле. Механизм Smart-компиляции предполагает дополнительную обработку объектного кода перед сборкой редактором связей.
Класс
template <список_типовых_параметров, параметры_константы> спецификация_класса;
Например
template <class T> class vector
{
T* body;
int size;
public:
Vector (int);
~Vector(int);
T& operator[](int);
T& elem(int);
}
vector <int> – конкретизация типа.
vector <int> v(10);
vector <int> a(25);
можно параметризовать и длину вектора
template <class vector T, int n> class vector
T body[n];
int size;
public:
Vector (int);
T& operator[](int);
T& elem(int);
}
vector <int,25>a; vector <int,10> v;
Деструктор в принципе уже не нужен, так как вектор в массиве, но нужно генерировать для каждой конкретизации по 2 функции ([] и elem), потому что размер вектора определяется константой. Директива typename <имя> означает, что <имя> является именем неизвестного типа. Ее используют в шаблонах, когда Т еще не известен (причем тоже не известно, описан ли в нем Х).
typename T::X;
T::X a;
Глава V Исключения
Исключения – аварийная ситуация.
В Visual Basic : ON ситуация оператор
На исключительную ситуацию вешается обработчик, осуществляющий ремонт на месте. При возникновении ошибки мы не знаем, где она произошла. В Unix-е все системные вызовы возвращают целое значение, если оно меньше 0 – ошибочная ситуация, иначе – нормальная.
int имя_вызова (...);
Например
if ((fd=open(...))<0)
{ обработка ошибочной ситуации }
else
{ нормальная ситуация }
Но этот метод плох тем, что логика аварийного кода не отделена от логики нормальной работы, текст программы увеличивается экспоненциальным образом. Можно использовать setjump(...) и longjump(...), но опять же пропадает структурированность обработчика, он вырождается в «пластырь».
Основные принципы обработки исключений в современных языках:
-
минимизация ущерба;
-
локализация;
-
разделение.
Проблемы при обработке исключений
-
определение исключения;
-
возбуждение исключения;
-
распространение исключения;
-
реакция на исключение.
exception – якобы тип данных.
Описание
имя_исключения : exception;
Существует зарезервированные идентификаторы исключений RANGE_EXCEPTION, ...
Оператор raise возбуждает исключение
raise имя_исключения;
После выполнения исключение распространяется. То есть, если в Р2 нет соответствующего обработчика, то исключение распространяется на Р1 и т.д. Вызов Р2 становится эквивалентным вызову raise. Если исключение не обрабатывается, то и в Р, то выдается посмертный дамп и адская программа завершается. Набор ловушек помещается в конце программного кода.