Скопин
ОСНОВАНИЯ
ПРОГРАММИРОВАНИЯ
УДК 519.682
Основания программирования
i
ii
Иными словами, как конкретные языки, так и алгоритмы служат для предла-
гаемого учебника в первую очередь иллюстративным материалом.
Внимание!
Базовые понятия
1
2
/*Язык C.*/
#include <stdio.h>
int main(void)
{printf("Hello World!");
return 0;}
3
4 ГЛАВА 1. ПОНЯТИЯ
Программа 1.1.2
//Java
public class HelloWorld {
public static void main (String[] args) {
System.out.println("Hello World!");
}}
Программа 1.1.3
(*Паскаль*)
program First (Output);
begin
writeln(’Hello World!’)
end.
Программа 1.1.4
comment Algol 68 comment
begin
println(‘Hello World!’)
end
начало
печатать(‘Hello World!’)
конец
(println(‘Hello World!’))
(печатать(‘Hello World!’))
1.1. ЯЗЫКИ И СИСТЕМЫ ПРОГРАММИРОВАНИЯ 5
Программа 1.1.5
Лисп:
( PRINT “Hello World!”)
Программа 1.1.6
Рефал
$ENTRY GO{=<Prout ’Hello World!’>};
Программа 1.1.7
Prolog
:-Print(’Hello World!’);
Сравним все эти программы. Что у них является общим?
1. Все они представлены в виде текстов: последовательностей символов,
размещенных на нескольких строчках.
2. В каждом языке имеется понятие строки (последовательности симво-
лов нефиксированной длины). Строки оформляются во всех языках при-
мерно одинаково: с использованием кавычек как обрамляющих симво-
лов. Правда, вид конкретных кавычек меняется от языка к языку.
3. Каждая из этих программ имеет конструкцию, выполнение которой при-
водит к распечатыванию строки.
4. Все они при их выполнении делают одно и то же: печатают строку
«Hello World!»
В чем же их отличия? Видны некоторые различия в записи, а также в пра-
вилах построения. Таким образом, напрашивается вывод, что, освоив один
язык программирования, можно понимать тексты программ на большинстве
других языков практически так же легко, как образованный русский человек
может читать украинский либо, в крайнем случае, польский (это чуть труд-
нее) текст.1
Казалось бы, что все эти программы, естественно, после преобразования
каждого текста в исполняемый код, приводят к одной и той же машинной
программе, исполняемой компьютером. Как мы дальше увидим, это не так.
Конечно же, и здесь могут быть исключения, но это верно практически для всех т. н.
языков высокого уровня
1
6 ГЛАВА 1. ПОНЯТИЯ
закрепляется их смысл.
Не все операционные языки мы относим к языкам традиционного типа. В частности, язык
Java является операционным, но из-за некоторых принципиальных отличий данного языка
3
/*Язык C:*/
#include <stdio.h>
int main(void)
{printf("Hello World!");
return 0;}
Строка
#include <stdio.h>
В принципе это не исключает переносимости (т. е. возможности исполнять программу
без всяких изменений в разных вычислительных обстановках), и уже были эксперименты
4
отражает то, что написанный текст программы должен быть расширен путем
вставки вместо этой строки текста, именуемого stdio.h, и уже такой расши-
ренный текст подается транслятору. одно из правил формирования текстов
программ на языке С). Таким образом, stdio.h является библиотекой периода
трансляции. Фактически stdio.h содержит все то, что нужно для организации
ввода и вывода данных, описывая компоненты библиотеки периода исполне-
ния.
Текст после #include <stdio.h> — это описание функции без параметров,
вырабатывающей целое (ее заголовок — int main (void)), которая печатает
строку:
// Java:
public class HelloWorld {
public static void main (String[] args) {
System.out.println("Hello World!");
}}
Строка
(* Pascal:*)
program First (Output);
1.1. ЯЗЫКИ И СИСТЕМЫ ПРОГРАММИРОВАНИЯ 11
begin
writeln(’Hello World!’)
end.
(*PASCAL*)
PROGRAM FIRST (OUTPUT);
BEGIN
WRITELN(’Hello World!’)
END.
begin
println(‘Hello World!’)
end
начало
печатать(‘Hello World!’)
конец
(печатать(‘Hello World!’))
ботает с полем зрения, которое уже не может быть прямо представлено как
совокупность ячеек вычислителя. Она проверяет, что поле зрения пусто, и
подставляет вместо пустого выражения то, что идет справа от знака равен-
ства: вызов стандартной функции, печатающей строку и опять очищающей
поле зрения. Поскольку в поле зрения функций больше не осталось, програм-
ма заканчивает работу, а поскольку поле зрения пусто, больше ничего не пе-
чатается.
Пояснение к программе 1.1.7.
:-Print(’Hello World!’);
1.1. ЯЗЫКИ И СИСТЕМЫ ПРОГРАММИРОВАНИЯ 13
Память Управляющее
0 устройство
+1
3
Процессор
К О1 О2
Канал связи
ние — далеко не единственные решения этого плана. Нарушение принципа здесь лишь ча-
стичное: ячейка любого из видов по-прежнему способна принимать значения разного типа,
по-прежнему смысл значения раскрывается только процессором. Заслуживают упоминания
и более радикальные архитектуры, предусматривающие тегирование (см. стр. 62), а также
полное отделение в памяти областей, в которые можно записывать данные и команды (в
обычном режиме выполнения программ процессору не разрешается записывать что-либо в
область команд, в результате повышается надежность программ).
1.2. МОДЕЛЬ ВЫЧИСЛЕНИЙ ФОН НЕЙМАНА И ТРАДИЦИОННЫЕ ЯЗЫКИ 25
академических исследований.
Отечественный опыт разработки машинно-ориентированных языков де-
монстрирует поддержку архитектуры, отличную от Intel-подобной. Укажем
на два проекта этого рода. Первый — язык Ярмо (аббревиатура: язык реа-
лизации машинно-ориентированный), построенный для ЭВМ БЭСМ-6 и от-
ражающий все современные веяния в языкотворчестве. О качестве и востре-
бованности этого языка можно судить хотя бы по тому, что было реализо-
вано несколько его версий. Второй пример — Эль-76, разработанный в ка-
честве аналога ассемблерного языка для многопроцессорного вычислитель-
ного комплекса Эльбрус. Оставаясь в целом фон Неймановской машиной,
архитектура этого комплекса далеко отходит от канонических принципов. В
частности, в ней предусмотрена аппаратная поддержка вызова процедур, сте-
ковая организация памяти и другие высокоуровневые средства программиро-
вания. Особенностью этой архитектуры является тегирование: каждое зна-
чение сопровождается тегом, т. е. описателем того, к какому типу относится
информационная часть значения. По существу это отказ от строго однород-
ной памяти. Все архитектурные особенности Эльбруса отражены в Эль-76,
что позволило рассматривать данный язык в качестве единственного инстру-
мента программирования системных программ. Конечно, нельзя говорить о
механическом переносе этого языка в архитектурную среду другого типа, а
потому время использования его, как и любого машинно-ориентированного
языка, ограничено временем жизни данной архитектуры12 .
8. Язык Ada. Он разрабатывался по заказу Министерства обороны США
на конкурсной основе с предварительным сбором и анализом требований,
с обширной международной экспертизой. По существу в нем воплощена по-
пытка определить язык программирования как экспертно обоснованный ком-
плекс средств программирования. На завершающем этапе конкурса приняли
участие около ста коллективов. В результате проверки соответствия предста-
вленных разработок сформулированным требованиям, для обсуждения об-
щественности было отобрано четыре языка, зашифрованных как Red, Green,
Blue и Yellow. В разной степени критике подверглись все четыре кандидата.
Особенно острым критиком оказался Э. Дейкстра, который камня на камне
не оставил от Red, Blue и Yellow, но чуть-чуть ‘пожалел’ Green, доказывая,
Это утверждение не противоречит долголетию С. Как уже отмечалось, данный язык сего-
дня навязывает архитектуру машин, которая бы не противоречила его эффективной реализа-
12
ции. Например, использование С в качестве базового языка для Эльбруса явно ограничивало
бы доступ ко многим развитым машинным возможностям.
32 ГЛАВА 1. ПОНЯТИЯ
го подхода стало появление языка Java, который был предложен как средство
программирования не для отдельного компьютера, а сразу для всех машин,
имеющих реализацию так называемой Java-машины — исполнителя про-
грамм на промежуточном языке более высокого уровня, нежели командный
язык обычных машины. Иными словами, провозглашается система програм-
мирования с явно и точно определенным промежуточным языком. Другая
особенность Java-проекта — его ориентация на Интернет программирова-
ние: поддерживается возможность построения приложений, запускаемых на
сервере от клиентской рабочей станции и взаимодействующих с клиентом
через произвольный браузер.
Схема трансляции с выделенным промежуточным языком, независящим
от исходного языка, очень даже не новая. В шестидесятые годы ее пытались
применять для сокращения расходов на разработку трансляторов (например,
в качестве промежуточного языка в США был разработан специальный язык
Uncol, в Советском Союзе для тех же целей предлагался язык АЛМО). Чисто
умозрительно можно представить ситуацию. Требуется реализация m язы-
ков на n машинах. В схеме без промежуточного языка в этом случае нуж-
но запрограммировать m × n трансляторов, тогда как использование такого
языка позволяет сократить это число до m + n: для каждого языка пишет-
ся транслятор в промежуточный язык (m) и для каждой машины создается
транслятор или интерпретатор промежуточного языка (n). К тому же, опять-
таки умозрительно, можно предположить, что затраты на ‘половинки’ транс-
ляторов сократятся. Все получается, если удается построить промежуточный
язык удовлетворяющий следующим условиям:
Выполнить эти условия весьма сложно даже для близких языков и машин.
Точнее сказать, что затраты на решение этих задач неизмеримо и неоправ-
данно превышают стоимость пресловутых m × n трансляторов. Поэтому по-
сле серии экспериментальных проектов идея промежуточного языка была
предана забвению. В проекте Java она возродилась, правда, в урезанном до
1.2. МОДЕЛЬ ВЫЧИСЛЕНИЙ ФОН НЕЙМАНА И ТРАДИЦИОННЫЕ ЯЗЫКИ 35
тем, что в прежние времена эту идею изучали в университетах, тогда как сегодня за нее взя-
лись промышленники.
В этой связи уместно следующее замечание. Если бы книга [68] не была бы сегодня би-
блиографической редкостью, то ее главу 2 «Рабочая программа» можно было бы рекомен-
14
довать в качестве первоначального введения для тех, кто желает изучить устройство Java-
машины. После прочтения этого изложения понимание Java-машины окажется более глубо-
ким.
36 ГЛАВА 1. ПОНЯТИЯ
Программа 1.3.2
/* начало блока */
int main (void)
{
scanf ( "d", &X );
X23 = X * X;
X23 = X23 * X;
Y = X23 * X23 * X;
printf (“\n7 power of %d is %d\n”, X, Y);
/* конец блока */
return 0;
}
38 ГЛАВА 1. ПОНЯТИЯ
Программа 1.3.3
/* начало блока */
int main (void)
{
Y = X * X * X;
Y = Y * Y * X;
printf ("\n7 power of %d is %d\n", X, Y );
/* конец блока */
return 0;
}
Строка
тексту программы (см. рис. 1.2, на котором простые стрелки изображают ука-
затели мест хранения нужных текстовых фрагментов, а жирные стрелки по-
казывают процесс включения файла в текст). Из схемы видно, что в С/С++/C#
подключение внешних библиотек — двухступенчатый процесс: в исходном
тексте программы указывается только заголовочный файл. Встраивание его
в тест позволяет транслятору корректно работать с именами, т. е. считать их
описанными и доступными в исходном тексте. Для выполнения транслируе-
мой программы нужно иметь следующую ступень: тексты или отдельно от-
транслированные фрагменты программ, представленных внутри заголовоч-
ных файлов. В С/С++ задачу обеих ступеней решает препроцессор.
В других языках подключение библиотек организовано по-разному. Так,
в Algol 60 понятие внешней библиотеки вообще никак не определялось. Счи-
талось, что достаточно говорить об отдельных процедурах. То же можно ска-
зать и про большинство других ранних языков.
Для внешних библиотек, которые определены языком, нет проблемы, как
указать их подключение — система программирования их “знает”, а потому
может считать их частью программы, которую программист писать не дол-
жен. Эта идея в законченном виде представлена в Алголе 68, в котором лю-
бая программа считается блоком, погруженным в стандартный блок, начина-
ющийся библиотечным вступлением и заканчивающийся библиотечным за-
ключением (см. § 1.1.1). Проблема может возникнуть, когда в распоряжении
программиста есть несколько библиотек, выполняющих похожие функции и
поименованных одинаково. В этом случае нужно явно указывать, какую из
библиотек подключать17 .
В ряде языков приняты более строгие соглашения: программисту пред-
писывается указывать источник подключения (подобно тому, как в С/С++/C#
указывается заголовочный файл) и конкретные подключаемые средства, в
дальнейшем используемые в программе. Если это соглашение оформляется
в виде языковой конструкции, то появляется возможность на уровне языка
конструировать библиотеки. В таком случае можно корректно подключать
не только процедуры и функции, но иные программные фрагменты (напри-
мер, описания переменных). В законченном виде этот подход представлен в
языке Modula-2, в котором программа рассматривается как набор модулей,
Именно это сделано в С/С++/C#, причем для всех, как стандартизованных языком, так
и для других доступных библиотек. С точностью до того, что подключение выполняется в
17
Файлы
Библиотека stdc заголовков
stdio.h
...
scanf
...
Исходный текст программы
Реализация string.h
scanf ...
... #include <stdio.h>
...
...
mem.h scanf
... ...
...
...
scanf
...
………………….
Рис. 1.4. Схема подключения модуля в языках Turbo Pascal и Object Pascal.
46 ГЛАВА 1. ПОНЯТИЯ
1.3.5. Действия
или
Y = X23 * X23 * X;
Y = X * X * X;
***
48 ГЛАВА 1. ПОНЯТИЯ
Для того, чтобы разобраться с тем, как порядок конструкций в тексте про-
граммы соотносится с порядком их исполнения абстрактным вычислителем
и с представлением абстрактного вычислителя в конкретном, целесообразно
рассмотреть понятия последовательного, параллельного и совместного ис-
1.4. ВЫЧИСЛЕНИЯ И ТЕКСТ 49
Действия окружения
Активизация main
В любом порядке
Выделение памяти для X Выделение памяти для Y
Чтение X
Y = X * X * X;
В любом порядке
Y = Y * Y * X;
Вывод X и Y
Завершение вычислений
Программа
Подключение
Заголовок Блок
внешних текстов
#include <stdio.h>
Y = Y * Y * X; Оператор присваивания
Оператор Операторы
Оператор
полнения.
При последовательном вычислении последовательность языковых кон-
струкций переходит в команды абстрактного вычислителя, исполняемые стро-
го в том порядке, который явно задан в тексте программы (именно так, в пер-
вом приближении, интерпретируется последовательность операторов, разде-
ленных ";", в языках С и Pascal).
Параллельное вычисление означает, что для выполнения двух и более ко-
манд абстрактного вычислителя выделяется соответствующее число процес-
соров, каждый из которых выполняет свою команду (в стандартном С по-
нятие параллельности отсутствует, в Алголе-68, Concurrent Pascal и других
языках параллельные участки часто выделяются отдельными программными
скобками, например, parbegin и parend; обрамляемые ими операторы могут
быть выполнены параллельно).
Даже если на машине всего один процессор, указание на параллельность
процессов часто полезно, поскольку оно позволяет организовать квазипарал-
лельное исполнение, при котором команды каждого из процессов исполняют-
ся в строгом порядке, но они могут перемежаться в абстрактном вычислителе
командами другого, квазипараллельно исполняемого, процесса. В частности,
это позволяет более производительно использовать время, которое процесс
тратит на ожидание завершения операции ввода-вывода. Например, при та-
ком исполнении отрезок программы
printf("Первый результат: %d, %d\n", X, Y);
Оператор 1;
...
того из подвыражений a*b, c*d, для которого уже готовы исходные данные.
Если a, b, c, d являются простыми переменными, то действительно безраз-
лично с точки зрения результата, в каком порядке их вычислять. Но вычи-
сление операнда может иметь побочный эффект, то есть вызывать не отно-
сящиеся к использующему оператору и не обозначенные явно действия. На-
пример, побочный эффект появляется при вызове функции, в теле которой
меняются значения глобальных переменных. Распознавать такие ситуации в
общем случае невозможно. И поэтому в руководствах по языкам можно часто
увидеть загадочную фразу «побочный эффект игнорируется», смысл которой
соответствует тому, что в данном месте могут быть элементы совместного
вычисления.
Явно и достаточно четко концепция совместного исполнения была прове-
дена лишь в Алголе 68, в котором был введен другой разделитель операторов:
“,” вместе с оставшимся как признак строго последовательного исполнения
“;”.
x := 1, y := 2, z := 3;
(1.1)
a[x] := b[y], d[z] := e[x];
На примере данного текста программы можно проиллюстрировать понятие
строгого совместного вычисления. При таком исполнении результаты фраг-
мента программы одни и те же при любой реализации совместного испол-
нения. В приведенном выше фрагменте совершенно безразлично, в каком
порядке присваивать значения переменным и в каком — элементам масси-
вов, лишь бы сначала были сделаны присваивания переменным, а затем —
присваивания элементам массивов. Казалось бы, такие случаи можно было
Если изучить стандарт языка Java, то оказывается, что он специфицирует в данном случае
слабое совместное исполнение (см. ниже) в чистом виде.
18
1.4. ВЫЧИСЛЕНИЯ И ТЕКСТ 53
Охраняемые команды:
if
A1 → S1 ,
(1.2)
... ,
An → Sn
fi
if
x 6 y → z := x,
y 6 x → z := y (1.3)
fi
return(z);
Если наши данные не являются числами и лишь предупорядочены (см., напр. [34]),
то не определено, какое значение получит z в итоге, но это нам и безразлично.
19
К сожалению, в общераспространенных системах таких средств не предусмотрено.
Мы в идущем ниже примере чуть-чуть изменили обозначения, чтобы привести их в боль-
шее единообразие с тем единственным случаем, когда совместные исполнения были явно
20
X := 1;
parbegin
X := X + 1;
Y := 5 + X;
parend;
с плавающей точкой! */
float P, q;
float D;
Адреса Команды
//команды, вычисляющие условие и
//помещающие результат в процессорный регистр S
m
m+1
ПЕРЕЙТИ К m+k+i+j
... ...
Программа 1.4.2
if ( D > 0 )
{
D = sqrt ( D );
X1 = r + D;
X2 = r - D;
printf ("\nX1 = %f, X2 = %f\n", X1, X2);
}
else if ( D == 0 )
{
X1 = r;
printf ("\nX1 = X2 = %f\n", X1);
return 0;
}
Оба приведенных выше представления условного оператора по сути изо-
морфны, и при каждом из них возникает одна и та же проблема. Рассмотрим
запись
if . . . then if . . . then . . . else . . . ;
К какому из if . . . then относится else? Есть два, с внешней содержательной
точки зрения совершенно равноправных ответа:
if . . . then
if . . . then . . .
и
else . . . ;
if . . . then
if . . . then . . . else . . . ;
Здесь использованы отступы и концы строк, чтобы отразить в записи, что к
чему относится. Решения данной проблемы известны следующие:
1. Запретить использование условного оператор в качестве действия T.
Такое решение, принятое в Алголе 60, приводит тому, что при необхо-
димости нужно превращать условный оператор в безусловный, обрам-
ляя его блочными скобками:
if . . . then begin if . . . then . . . else . . . end;
if . . . then begin if . . . then . . . end else . . . ;
нии (x + y) ∗ z, и литералы.
1.5. РАБОТА СО ЗНАЧЕНИЯМИ 61
пустимая операция.
62 ГЛАВА 1. ПОНЯТИЯ
Тег Значение
определяет тип того, что хранится в основной части ячейки (тип значения)
<Int a> + <Float b> → <Float aF > + <Float b> Выполняется
процессором
где loc type — это выражение, вырабатывающее новое значение типа ref type
(локальный генератор адреса для размещения значений типа type в качестве
содержимого по указанному адресу; это — один из исключительно редко
встречающихся случаев, когда переменная задается не именем, а значением
некоторого выражения). Таким образом, по-прежнему определяется констан-
та, значением которой обладает имя Имя1.
1.5. РАБОТА СО ЗНАЧЕНИЯМИ 65
Но это значение есть адрес (о чем говорит служебное слово ref), и появля-
ется возможность использовать Имя1 как переменную: можно присваивать
ей значение типа type.
Аналогично
ref type Имя2;
сокращение для
ref ref type Имя2 = loc ref type;
Таким образом, есть возможность присваивать Имя2 значение типа ref type.
Иными словами, Имя2 — это переменная, предназначенная для размещения
в ней адресов, или, что то же самое, имен других переменных.
Таким образом, последовательность предложений
real A = 53/5;
real B;
описывает константу A со значением, равным результату деления числа 53 на
5, и переменную B вещественного типа. Можно записать и выполнить при-
сваивание B:=A, но не наоборот. Если же записать
real A := 53/5;
real B;
то A оказывается переменной и присваивание A:=B становится законным.
В языке С все, казалось бы, проще. Понятие констант и переменных опре-
деляется на уровне соглашений об их отношении к памяти вычислителя. Опи-
сание
int X, Y;
определяет имена X и Y как переменные, способные принимать целочислен-
ные значения, а
float X, Y;
как вещественные переменные. Но, как и обычно, иная простота хуже воров-
ства. За недоработку в базовых понятиях приходится многократно платить
целой серией соглашений, которые сейчас будут описаны для простейшего
случая, и которые отдельно и достаточно сложно вводятся для более сложных
случаев, возникающих, в частности, в объектно-ориентированном програм-
мировании.
Для описания соглашений об отношении между переменными и памятью
вычислителя в C вводится понятие класса памяти, отражающее различные
66 ГЛАВА 1. ПОНЯТИЯ
Понятие класса памяти подтверждает тот факт, что С создавался как язык
программирования для вычислителя вполне определенной архитектуры. Сей-
час язык C++ реализован практически для всех архитектур. Но для них, с
одной стороны, некоторые из конкретных средств С оказались избыточны-
ми (например, регистровые переменные на архитектуре Intel), с другой сто-
роны, для некоторых из ценных качеств, предоставляемых архитектурно, в
С++ нет средств для их эффективного использования (примером является те-
гирование). В целом в языке C и его наследниках догматизируется принцип
однородности памяти.
Особенно вредной догматизация этого принципа оказалась в случае кон-
стант. Поскольку с точки зрения процессора традиционной архитектуры кон-
тейнер, содержащий константу, ничем не отличается от контейнера, в ко-
тором находится переменная того же типа, сам язык С дает возможность
использования только литералов. Однако с помощью препроцессора можно
приписать константам имена, что широко применяется на практике. Соот-
ветствующий пример показан в программе 1.5.1.
Программа 1.5.1
и ссылки:
int **a;
int &r;
68 ГЛАВА 1. ПОНЯТИЯ
(A + B) + C и B + (C + A)
Синтаксис, семантика и
прагматика языка
программирования
69
70 ГЛАВА 2. ССП
Определение 2.1.1.
Синтаксис алгоритмического языка — совокупность правил, позволяю-
щая формально проверить текст программы, выделив тем самым множество
синтаксически правильных программ, и разбить эти программы на составля-
ющие конструкции и в конце концов на лексемы.
Семантика алгоритмического языка — соответствие между синтаксиче-
ски правильными программами и действиями абстрактного исполнителя, по-
зволяющее определить, какие последовательности действий абстрактного ис-
полнителя будут правильны в случае, если мы имеем данную программу и
данное ее внешнее окружение.
Под внешним окружением понимаются характеристики машины, на кото-
рой исполняется программа (точность представления данных, объем памяти,
другие программы, которые можно использовать при выполнении данной, и
т. д.), и потоки входных данных, поступающие в программу в ходе ее испол-
нения.
Конец определения 2.1.1.
Задаваемое семантикой соответствие между входными данными, програм-
мой и действиями, вообще говоря, определяется лишь полным текстом про-
граммы, но для понимания программы и работы над ней необходимо, что-
бы синтаксически законченные фрагменты программы могли интерпретиро-
ваться автономно от окружающего их текста.
При создании описания языка следует стремиться к выполнению следу-
ющих требований:
определять нет смысла, поскольку за понятием КС-грамматики, при внешней его простоте,
стоит достаточно серьезная теория. Эта грамматика представляется во многих формах (син-
таксические диаграммы, металингвистические формулы Бэкуса-Наура либо расширенные
металингвистические формулы), и, как правило, сопровождает любое систематизированное
изложение конкретного языка. Любое такое конкретное представление КС-грамматики до-
статочно понимаемо.
74 ГЛАВА 2. ССП
ского языка, обрамляемые угловыми скобками < и >. Эти слова и сочета-
ния слов называются наименованиями понятий.
что смысле.
Используя контекстно-свободную грамматику, можно следующим обра-
зом записать определения для конструкций, уже показанных к настоящему
моменту в примерах программ:
<программа /* на языке С*/> ::= <заголовок> [ <описание функции /* одна
из которых имеет имя main */> ]+
<заголовок /* это определение упрощено4 */> ::= [<объявление подключа-
емого файла>]*
<объявление подключаемого файла> ::= “#include” <указание имени фай-
ла>
<указание имени файла> ::= “<” <наименование файла> “>” |<наимено-
вание файла>
<наименование файла> ::=
<имя файла> [ “.” <расширение> ]?
Комментарий /*это определение упрощено*/ здесь и далее означает, что в стандарте языка
данное понятие определено более полно. Некоторые из таких понятий в дальнейшем будут
4
дополняться.
2.1. СИНТАКСИС, СЕМАНТИКА 77
<имя функции > “(” [ <параметры> [“,” <параметры> ]* ]? “)” “{” <описания>
<параметры> ::= <описатель> < имя параметра > [ “,” <имя параметра>
тор> ]?
<условие> ::= <выражение >
<пустой оператор>
<имя функции>
<тип функции>
{<описания> <операторы>}
(<параметры>)
<описание функции>
Следуя этой точке зрения, мы, говоря о модели программы, всегда имеем в
виду ее образ в виде команд абстрактного, а не конкретного вычислителя.
Понятие модели вычислений языка естественно распространяется на слу-
чаи, когда используются библиотеки программ. Библиотеки, стандартизован-
ные описанием языка, можно считать частью реализации языка независимо
от того, как реализуются библиотечные средства: на самом языке или нет.
80 ГЛАВА 2. ССП
Показательный пример такого рода дает язык FORTRAN. В этом языке разрешается ис-
пользование идентификаторов с пробелами (причем пробелы внутри идентификаторов про-
6
Как он это ухитряется делать, рассматривать нет смысла, но гораздо практичнее и безопас-
DO I=1,. . .
нее запретить пробелы в идентификаторах, что сегодня можно считать стандартом почти
для всех языков программирования.
2.2. ПРАГМАТИКА 81
§ 2.2. ПРАГМАТИКА
и
Inc (<переменная>)
числений языка, так как для нее постулируется, что язык является машинно-
ориентированным и отражает в себе особенности архитектуры вычислитель-
ного оборудования, а команды увеличения и уменьшения на единицу предо-
ставляются программисту на уровне оборудования достаточно часто.
Если же рассматривать Turbo Pascal как правильное расширение стан-
дартного языка Pascal, не содержащего обсуждаемые команды из-за стрем-
ления к минимизации средств, то эти команды есть не что иное, как подсказка
транслятору, как надо программировать данное вычисление. Следовательно,
указанные операторы для данного языка нужно относить к прагматике. 8
Семантическая прагматика — это уже упомянутые определения того,
что в описании языка оставлено на усмотрение реализации или предписыва-
ется в качестве вариантов задания вычислений.
Пример 2.2.2. Стандарт языка Pascal утверждает, что при использовании
переменной с индексом на уровне вычислений контролируется выход индек-
са за диапазон допустимых значений. Однако в объектом коде постоянные
проверки этого свойства могут оказаться накладными и избыточными (на-
пример, когда можно гарантировать соответствующие значения индексов).
Стандарт языка для таких случаев предусматривает сокращенный, т. е. без
Вообще говоря, данная подсказка избыточна для современных трансляторов, поскольку
распознать шаблон вида
8
Даже сам автор данной программы через некоторое время не поймет, почему
же она у него так себя ведет. . .
Конец примера 2.2.3.
Как это ни странно, подобные построения используются в практике програм-
мирования на С: они применяются, чтобы в одном общем тексте задать не-
сколько вариантов выполняемых программ, которые разграничиваются при
работе препроцессора, т. е. до выполнения.
Наложение команд препроцессора на текст программы — это смешение
двух моделей вычислений: одна из них — модель базового языка С, другая —
модель препроцессора.9 В результате программист при составлении и изу-
чении программ вынужден думать на двух уровнях сразу, а это и трудно, и
провоцирует ошибки.
Модель вычислений препроцессора можно охарактеризовать следующими свойствами.
Исходные перерабатываемые данные — это текст (любой структуры, не обязательно на язы-
9
ке С), в котором имеются строки, начинающиеся с символа ‘#’. Такие строки представляют
команды, управляющие работой препроцессора. Например,
дает указание препроцессору на то, что в оставшейся части текста идентификатор two дол-
#define two 2
АБСТРАКТНОЕ И КОНКРЕТНОЕ
ПРЕДСТАВЛЕНИЯ ПРОГРАММЫ
§ 2.3.
X = a ∗ b + c ∗ d;
X = (a ∗ b) + (c ∗ d);
X = ((a ∗ b) + (c ∗ d));
2.3. ПРЕДСТАВЛЕНИЯ ПРОГРАММЫ 87
A + B ↔ B + A.
+ *
a + + +
* d a b c d
b c
a+b*c+d (a + b)*(c + d)
a b c d
a + (b*c) + d
Рис. 2.2. Абстрактно-синтаксическое представление выражений
90 ГЛАВА 2. ССП
printf
Оператор
присваивания
Получатель Источник
Адрес Выражение
Выражение,
Выражение Выражение Адрес
Сложение Вычитание Переменная
+ + Имя
Операнд Операнд
Выражение Выражение
...
Выражение
Сложение
Операнд Операнд
Операнд Операнд
Выражение Выражение
Переменная Переменная
Имя = b Имя = c
Операторы Совместные
операторы
Последовательно Совместно
выполняемые выполняемые
* *
Оператор Оператор
Оператор
условный
Оператор
условный
?
Условие Действия T Действия F
Выражение Операторы Операторы
Выражение, Выражение,
Оператор Оператор
Вызов Имя
функции функции
Имя * *
функции Параметр Параметр
Имя
Выражение Выражение
Имя
(a) (b)
...
if ( D > 0 )
{
D = sqrt ( D );
X1 = r + D;
X2 = r - D;
printf ("\nX1 = %f, X2 = %f\n", X1, X2);
}
else
...
…
Усло вный
Имя = printf
Имя = D Значение = 0
Параметр Параметр
Получатель Источник Переменная
Константа
Получатель Источник Переменная —
Адрес Имя
функции
Имя = X2
Операнд Операнд
Имя = D Переменная Переменная
Получатель Источник Параметр
Имя = sqrt
Переменная + Имя = X2 Переменная
Имя = r Имя = D
Операнд
Переменная Операнд Операнд Имя = X1
Переменная Переменная
для его точного определения нужно задать его синтаксис и семантику. Мето-
ды определения синтаксиса рассматриваются во вводных курсах программи-
рования для будущих профессионалов. Хотя полноты определения синтакси-
са обычно не достигают, но изложение этих методов достаточно последова-
тельное и строгое (как Вы могли увидеть в предыдущих главах данной кни-
ги). Даже если нечто объясняется на пальцах вместо кропотливого точного
определения (как, например, соглашения о том, какое описание соответству-
ет какому вхождению идентификатора), об этом говорится прямо.
Семантику же обычно рассказывают на поверхностном уровне, без те-
ни точных определений, в выражениях типа «При исполнении присваивания
x:=S старое значение, содержавшееся в памяти, обозначенной именем x, ис-
пользуется для вычисления S, после чего заменяется на полученное значе-
ние.» Это скорее беда, чем вина начальных (да и профессиональных) курсов
программирования. Ведь смысл — многоаспектное, крайне сложное и не-
формализуемое понятие. Это означает, что любое его уточнение приемлемо
лишь для некоторых целей и отказывает в других случаях. Но это не озна-
чает, что формализовывать семантику не нужно. Ведь если мы откажемся от
ее формализации, мы тем самым сползем на позицию первых языков типа
FORTRAN: смысл выражений языка определяется тем, как они переводятся
транслятором и исполняются компьютером.11
Далее, поскольку алгоритмические языки делались прежде всего не для
удобства определения их свойств, а для удобства их использования, работа-
ет концептуальное противоречие, которое вы могли бы уже заметить в кур-
се, например, логики: чем удобнее представление формального понятия для
применения, тем тяжелее его строгое формальное определение. Определе-
ние семантики реальных алгоритмических языков намного сложнее семан-
тик математических языков (хотя и намного проще семантик естественных
языков). Оно находится как раз на той грани, когда считанные по пальцам
профессионалы еще могут создать точное и полное определение и восполь-
зоваться им, но при этом практически все их интеллектуальные ресурсы ухо-
дят на выработку и освоение данного понятия. Соответственно, данное точ-
ное определение задает лишь значение либо действие конструкций языка,
Конечно, такое определение семантики алгоритмического языка в принципе является не-
погрешимым для каждой конкретной его реализации, применяемой в конкретной среде про-
11
граммирования. Но оно, мало того, что ничего не дает для осмысленного применения чело-
веку, еще и меняется непредсказуемо при изменениях версии или настроек транслятора и
всей вычислительной системы.
2.4. ПРИНЦИПИАЛЬНЫЕ ТРУДНОСТИ, СВЯЗАННЫЕ С СЕМАНТИКОЙ 103
Стили программирования,
или программирование с
птичьего полета
106
3.1. СТИЛИ ПРОГРАММИРОВАНИЯ 107
Предупреждение!1
То, что в данном тексте называется “методикой программирования”, в ли-
тературе традиционно называется “методологией”, а то, что у нас обознача-
ется этим словом, называется “парадигмой программирования”.
Остановимся на методиках. Они направлены на поддержку деятельности
коллектива, работающего с программой, в том числе и ее автора (авторов).
Поскольку методики включают в себя организационные и административ-
ные аспекты, они чаще всего даются в виде рецептов, следование которым, по
мнению их разработчиков, приводит к хорошим результатам. Обычно пред-
полагается (иногда об этом говорится явно), что методика фиксирует опыт
практической разработки хороших и неудачных программ. Однако разные
методики чаще противоречат друг другу, чем согласуются, а потому появля-
ется конкуренция между ними за сферы влияния. В рекламных целях опыт,
на базе которого возникла методика, затушевывается, и утверждается, что
методика годится на все случаи жизни. То же самое, хоть и не столь регуляр-
но, происходит и с методологиями. Это ведет к фетишизации, которая про-
воцирует применение методологий и методик в неподходящих ситуациях, а
следствием этого является их дискредитация. В результате эти действитель-
но полезные методологии и методики порой отвергаются на некоторое время.
Важнейший аспект стиля и соответствующей ему методологии: запре-
щения и самоограничения. Необходимо твердо усвоить, что многие базо-
вые концепции плохо согласуются друг с другом. В результате, будучи со-
единенными вместе, они утрачивают свои лучшие стороны, зато многократ-
но усиливают худшие (как говорят, они концептуально противоречат друг
другу). Поэтому каждый стиль отгораживается от тех конструкций, которые
концептуально противоречат ему (если уж вам такая конструкция необходи-
ма, выносите ее в под-, над- или сопрограмму)2 . Большой бедой является то,
что, запрещая не согласующиеся со стилем конструкции, практически все-
гда представляют их как абсолютно плохие, хотя на своем месте они часто
Заметим, что в программировании происходит систематическая ‘возгонка’ понятий. То,
что в других местах называется орудием, здесь называется методом; то, что называется мето-
1
дом или методикой, называется методологией; то, что называется методологией, возводится
в ранг парадигмы, и т. д. В данном пособии мы придерживаемся выражений, более адекват-
но передающих смысл используемых понятий и согласующихся с общечеловеческой прак-
тикой.
2
На худой конец, используйте комментарии, чтобы явно выделить отступления от стиля.
3.1. СТИЛИ ПРОГРАММИРОВАНИЯ 109
великолепно работают.
Понятие стиля программирования требует глубокой концептуальной про-
работки. При этом требуется обобщение большого количества практическо-
го материала и работа на стыке нескольких сложных теоретических напра-
влений. Из-за трудности задачи и во многих случаях из-за недостаточности
имеющегося теоретического и практического аппарата, анализ ряда стилей
вообще не делался. Поэтому в данном курсе мы приводим только извест-
ные нам результаты анализа, излагая их в как можно более доступной фор-
ме. Знать эти результаты особенно важно из-за того, что они дают не толь-
ко оптимистические заключения, но еще и серьезные предупреждения. Эти
предупреждения гораздо важнее для практика, чем гладенькие результаты о
принципиальной возможности что-то сделать (можно сравнить с прохожде-
нием программы через транслятор: то, что она успешно оттранслировалась,
почти ничего не говорит о ее правильности, а вот выданные при трансляции
предупреждения часто содержат ценнейшую информацию).
Так что в отношении стилей возникает та же ситуация, которая была с по-
нятием программы до появления цельной системы концепций и разделения
конкретных и абстрактных аспектов: несущественные особенности конкрет-
ного представления часто мешают понять идейное единство. С другой сто-
роны, формальное внешнее сходство затушевывает серьезнейшие концепту-
альные различия. Как говорится, за деревьями не видно леса.
Следует учесть, что разные стили ориентированы на разные модели вы-
числений, и, соответственно, система программирования выглядит по-раз-
ному для тех, кто привык к одному или к другому стилю, причем одни и те
же слова, как правило, приобретают разные значения, и могут возникнуть
серьезнейшие трудности при передаче опыта, при общении программистов,
при взаимодействии созданных ими систем.
Обратим внимание на другую сторону стиля. Составление программы —
процесс, гораздо в большей степени аналогичный составлению текста на
естественном языке, чем это может показаться на первый взгляд. Конечно,
речь идет не о художественных текстах, а о тех, которые читают, чтобы по-
лучить информацию. При работе над такими текстами понимание того, кто
именно будет его читать, просто необходимо для качественного изложения3 .
Поэтому рассмотрим читателей программы.
Самый очевидный читатель программы — это вычислитель, для которого
А непонимание и неучет читателя, соответственно, один из распространеннейших недо-
статков.
3
110 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
очередь, понимается как: ‘всегда, если задача не заставляет нас явно заняться этим вопро-
сом’. А задача определяется как цель в заданных ограничениях, и тот компонент задачи, ко-
торый чаще всего заставляет заняться вопросами эффективности — это ограничения.
3.1. СТИЛИ ПРОГРАММИРОВАНИЯ 111
на шпильках.
112 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
языка. Кроме них, есть еще круг вопросов, ответы на которые устанавливают
связи стиля программирования с методами проектирования программы:
• Как структурировать априори однородную память?
• На какие логические и физические части должна разбиваться програм-
ма?
• Как преодолевать сложность процесса разработки программного обес-
печения?
• За счет чего можно достичь повторного использования накопленных
программных решений и облегчать модификации программ?
Ответы на подобные вопросы формирут методологии и методики, реализу-
ющие стили программированния.
Из анализа опыта работы с фон Неймановской моделью вычислений вы-
являются свойства операционных программ, которые при надлежащей ин-
терпретации применимы также для характеристики стилей программирова-
ния в нетрадиционных моделях вычислений. В частности, для этих моделей
можно говорить об уровнях абстрактности описания данных и действий, о
глубине структурированности действий и данных, о связи структурирован-
ности с содержательным смыслом переработки данных, о степени развет-
вленности вычислений (аналоги рекурсии, условных конструкций и циклов
операционных языков). Возникает контекст вычисления, можно оценивать
степень глобальности и локальности действий и данных. Поэтому возмож-
но проникновение стилей, сформировавшихся в рамках фон Неймановской
модели, в нетрадиционное программирование.
Более того, привязанность стиля к базису часто переоценивается. Напри-
мер, в книгах [37, 77] приведены методики, как структурно программиро-
вать для столь неструктурных языков, как старые ассемблеры, FORTRAN и
• Программирование от состояний;
• Структурное программирование;
• Сентенциальное программирование;
• Программирование от событий;
• Функциональное программирование;
• Объектно-ориентированное программирование;
• Программирование от переиспользования;
• Программирование от шаблонов.
a b c d
4
1 2 3 4 1 Q P
2 3 4 1 2
3 4 1 2 3 R
1 3
Здесь состояния идентифици-
4 1 2 3 4
A
◦
A∨B
A B
f g B
B ◦ C
B∨C
x:=a
y:=a
x:=f(x)
P(x)
1 0
x:=f(x) stop(x,y)
P(x)
1 0
y:=f(y)
x:=f(x)
P(y)
P(x) 1 0
1 0
y:=f(y)
x:=f(x)
P(y)
P(x) 1 0
1 0
y:=f(y)
x:=f(x)
P(y)
P(x) 1 0
1 0
y:=f(y)
x:=f(x)
P(y)
P(x) 1 0
1 0
Error
СТРУКТУРНОЕ ПРОГРАММИРОВАНИЕ —
САМЫЙ РАСПРОСТРАНЕННЫЙ СТИЛЬ
§ 3.3.
в них совпадают.
126 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
ческим стандартом.
3.3. СТРУКТУРНОЕ ПРОГРАММИРОВАНИЕ 127
торые не имеют никакого отношения к реальной действительности. Так что при переходе от
уровня рассмотрения программы самой по себе к уровню программы как части архитекту-
ры реальной человеко-машинной системы идеальные и реальные объекты порою меняются
местами.
128 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
◦ f
Q g P
◦
Программа 3.3.1
Программа 3.3.2
вых средств завершения конструкций была осознана еще в начале 70-х гг.
3.3. СТРУКТУРНОЕ ПРОГРАММИРОВАНИЕ 131
E3 ⇒ {O2.hE3, O3.hE3}
Встраивание,
вызов и т.д.
(любой смысл)
Модули (библиотеки)
a) составление последовательного текста программы
Стандартизованный текст
программы (проект)
Встраивание
(любой смысл)
Подготовка данных;
{
Программа 3.6.2
стейших случаях. Например, если процесс долго ждет своей очереди на ис-
полнение, то естественно постепенно поднимать его приоритет. Но тогда при-
оритет такого «пользовательского» действия может превзойти приоритет си-
стемного процесса, и остается либо статически задавать каждому процессу
допустимый максимальный приоритет и мы вновь попадаем в ту же ловушку,
либо задавать разным классам процессов приоритеты просто несравнимые
по порядку, чтобы ни при каком нормальном исполнении приоритет пользо-
вательского процесса не дорос, скажем, до 109 , за которым начинаются при-
оритеты срочных системных демонов.
Заметим, что назначение приоритетов операционной системой, «сверху»,
принципиально ничего в данной картине не меняет. С точки зрения пользо-
вательских программ эти приоритеты все равно статические.
Рассмотрим теоретически, какие классы программ можно описать при
помощи статических и динамических приоритетов. Оказывается, при помо-
щи статических приоритетов, являющихся натуральными числами, невоз-
можно описать даже примитивно-рекурсивные композиции исходных дей-
ствий21 .
Таким образом, чисто концептуально натуральных чисел не хватает для
описания приоритетов, но оказывается, что для них хватает ординалов (см.
курс математической логики). Поскольку ординалы до ε0 имеют хорошую
вычислительную модель (см., напр. [63]), можно рассматривать целочислен-
ные приоритеты как конкретную реализацию абстрактного класса таких ор-
диналов.
А в случае реально распределенных или реально параллельных действий
проблема приоритетов еще важнее и еще сложнее. Часто управление по при-
оритетам — единственный шанс справиться со сложностью такой нелиней-
ной во времени системы. Тут уже и линейно упорядоченного множества при-
оритетов не хватает.
В целом программирование от приоритетов является мощным, но спе-
цифическим орудием для описания глобальных совместных, параллельных
или распределенных процессов. Его элементы проникают и в традиционные
системы программирования в виде обработки исключительных ситуаций в
программах. Здесь, как и в событийном программировании с обособленными
реакциями, всего два приоритета, которые налагаются на структуру исполь-
зующей конструкции. Противоречивость этого средства со структурным про-
Правда, здесь мы отвлекаемся от реальных ограничений, и считаем 1010 столь же легко
10
граммированием только в том, что не всегда ясно, что делается далее, когда
обработка исключения завершается. В сравнении со структурными перехо-
дами такое расширение представляется более искусственным.
λx.x(x)(λx.x(x)).
third(i) = i%3.
Для преподавания надо найти небольшую предметную область, для которой можно было
бы проделать системный анализ и построить обозримую открытую модель. Это еще возмож-
но (например, в качестве таковой можно рассматривать игру в ‘очко’ с учетом различных
стратегий игроков и их психологии.) Но на таких задачах удается показать лишь некоторые
преимущества и некоторые стороны объектной модели, которые почти уравновешиваются
недостатками, связанными с резким увеличением объема сопровождающей моделирующую
программу информации.
Можно привести содержательную аналогию. Если построен мощный танк, то даже для
того, чтобы его разместить, требуется значительное пространство, а уж чтобы ему развер-
нуться и показать свои возможности. . . Естественно, что его нельзя опробовать на столе в
комнате директора или в аудитории, рассчитанной на 10 человек.
154 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
принимаете на себя роль обучаемого, а авторы данной книги — роль наставников. Человек,
выступающий в разных ролях, на одни и те же раздражители реагирует по-разному. Если
же человек играет не ту роль, которую ожидают от него окружающие, то он ведет себя (с
точки зрения окружающих) неадекватно, что зачастую рассматривается как один из видов
асоциального поведения.
3.9. ТРИ ТЕХНОЛОГИЧЕСКИХ СТИЛЯ ПРОГРАММИРОВАНИЯ 155
Внимание!
жаргоне, на парсер и обработчик). Это как раз то, что дает объ-
ектная модель документа.
Если D~x = D~x × D~x , причем значения аргументов из D~x фиксированы (для
0 00 00
Y = V(P, X).
MC(P, X) v X
и
X ∪ (PEV(P, X, Y)2 = Y.
А вот это математическое определение прочитайте и разберитесь в нем сами!
Русский программист обязан хорошо владеть математическим языком, иначе
он неконкурентоспособен на мировом рынке, особенно после 30 лет.
В данном определении нет никакой привязки к модели вычислений язы-
ка: смешанные вычисления можно определить везде, где определено поня-
тие алгоритма абстрактного вычисления языка. Ссылка на память не озна-
чает действий в фон Неймановской модели, сравните наше определение с
описанием теории рекурсивных схем в § A.6. Задача специализации состоит
в том, чтобы реализовать требуемое деление алгоритма на остаточные дан-
ные и остаточную программу и предварительное выполнение той его части,
которая обусловлена сведениями о перерабатываемых данных.
Пример 3.9.1. Пусть требуется построить программу, вычисляющую функ-
цию λX. X n . Это означает, что программа должна работать для любых зна-
чений X и для конкретного фиксированного значения n. Начнем со следу-
ющей более универсальной программы, реализующей функцию λX, n. X n
(для простоты иллюстрации здесь и далее приводится лишь фрагмент про-
граммы, касающийся существа дела):
Программа 3.9.1
3.9. ТРИ ТЕХНОЛОГИЧЕСКИХ СТИЛЯ ПРОГРАММИРОВАНИЯ 173
Y := 1;
while n>0 do
begin if n MOD 2 = 1
then begin Y :=Y * X; n := n - 1 end;
n := n DIV 2; X := X * X
end
Y := X; X := X * X; X := X * X; Y := Y * X (3.1)
Y := 1 * X; X := X * X; X := X * X; Y := Y * X; X := X * X (3.2)
автор.»
3.10. ОБЩИЕ ВЫВОДЫ 179
то из-за отсутствия удобных средств привык чесать левой ногой за правым ухом или спать
на гвоздях, он зачастую будет делать это, даже когда необходимость пройдет, а если имеет
власть, то и других заставлять делать то же. Оглянитесь вокруг, и Вы увидите целую кучу
таких примеров.
180 ГЛАВА 3. ПРОГРАММИРОВАНИЕ С ПТИЧЬЕГО ПОЛЕТА
В свзязи с этим можно заметить, что популярная в настоящее время технология RUP явля-
ется средством автоматного представления динамики графики и следующей из нее динамики
34
§ 4.1. ВВЕДЕНИЕ
181
182 ГЛАВА 4. ЖИЗНЕННЫЙ ЦИКЛ
Использование
Разработка
Продолжающаяся разработка
(сопровождение)
• сопровождение.
Определение требований
Спецификации
Реализация
Тестирование
Сопровождение
Фаза эксплуатации и
сопровождения
Развитие
Определение требований
Спецификации
Проектирование
Реализация
Тестирование и отладка
Эксплуатация и сопровождение
является стимулом творческой деятельности человека. Более того, задачу, которая требует
творческого подхода, лучше всего формулировать в виде проблемных противоречий, макси-
мально обостряя их. Именно тогда можно надеяться на то, что будет найдено действитель-
но хорошее и адекватное решение. Методология использования проблемных противоречий
в творческих рассуждениях была развита Г. С. Альтшуллером и советской школой теории
решения изобертательских задач (ТРИЗ). Программистам и особенно преподавателям про-
граммирования настоятельно рекомендуется прочитать книгу Г. С. Альтшуллера [4]. Хотя
методы ТРИЗ в большинстве своем прямо не могут быть применены при решении програм-
мистских задач, поскольку они в основном направлены на поиск элементарных решений,
преодолевающих «сопротивление материала», методология подхода исключительно полез-
на для любого творческого человека, занимающегося созданием реальных систем (именно
этим программисты, ученые и инженеры отличаются от гуманитариев).
4.2. ТРАДИЦИОННОЕ ПРЕДСТАВЛЕНИЕ 189
Определение требований
Спецификация требований:
• системные требования
Проектирование
Чем заканчиваются этапы
Реализация:
• кодирование и автономная отладка
Эксплуатация и сопровождение
Определение требований
Спецификация требований:
• системные требования
Проектирование
Чем заканчиваются этапы
Реализация:
• кодирование и автономная отладка
Эксплуатация и сопровождение
1. планирование,
2. разработка,
3. обслуживание,
4. выпуск документации,
5. испытания,
6. поддержка,
196 ГЛАВА 4. ЖИЗНЕННЫЙ ЦИКЛ
7. сопровождение.
Пополнение базового
окружения проекта
Окончание работ
↔
Рис. 4.9. Фазовое измерение модели жизненного цикла при итетаривном раз-
витии проекта
4.3. ИТЕРАТИВНЫЕ МОДЕЛИ ЖИЗНЕННОГО ЦИКЛА 205
тем, когда речь идет о совместной поддержке нескольких версий (а именно такая ситуация
типична для итеративного проектирования) окончание работ игнорировать нельзя.
Явное отступление от этого постулата явилось одним из методологических достижений
объектно-ориентированного проектирования, значение которого выходит далеко за рамки
6
собственно ООП.
4.3. ИТЕРАТИВНЫЕ МОДЕЛИ ЖИЗНЕННОГО ЦИКЛА 207
Пополнение базового
окружения проекта
Окончание работ
Контрольные
точки
(события): 0 1 2 3 4 6 7 8 9 10 11 12
V
Пл| Ан| Ко | Пр| Те | Оц
IV
Пл| Ан| Ко | Пр| Те | Оц
III
Пл| Ан| Ко | Пр| Те | Оц
II
Пл| Ан| Ко | Пр| Те | Оц
I
Пл| Ан| Ко | Пр| Те | Оц
Время
Оценка Планирование
Реализация Анализ
(программирование и
тестирование)
Конструирование
требованиями.
8. Требования изменяются
Фиксируемые в заказе на разработку требования к системе, претенду-
ющей на широкую сферу применения и долгую жизнь, не являются за-
стывшими и незыблемыми. Они изменяются как из-за учета новых фак-
торов и пожеланий, так и в связи с выявлением особенностей проекта в
ходе его разработки. Следовательно, необходимо так строить аналити-
ческую работу, чтобы иметь возможность оперативно изменять полу-
чаемые результаты, учитывать в них изменения и дополнения исходной
информации;
2. Унифицированные представления
требований
Табстр
Глоссарий проекта
4. Модельные представления
уровня анализа
5. Модельные представления
уровня конструирования
6. Программные представления
7. Документные представления
Требование реализуется на
более поздней итерации
Пополнение базового
окружения проекта
Окончание работ
Контрольные ← Использование
точки ↔
(события): 0 1 2 3 4 5 6 7 8 9 10 11 12
Анализ нового требования Требование реализуется на текущей итерации
Требование
отклоняется ← (a) Требование поступило, начало мини-цикла
Начальная Продолжение
фаза проекта
проекта ← Мини-циклы разработки сценариев →
← Автономная← Интеграция →
работа →
Пополнение базового
окружения проекта
ние →
← Программирование →
Оценка
Контрольные
точки
(события): 0 1 2 3 4 5 6 7
Выбор сценариев для Начата ←5’’ Сценарии ←8 Требования к следующей
реализации сделан 2’→ интеграция реализованы, итерации приняты
сценариев 5’→ базовый набор
Совместная работа
требований
с выбранными
определен
сценариями
Модели сценариев построены 3’→ Демонстрационные испытания
завершены 9 →
Немедленная
Контрольные реализация
точки
(события): 0 1 2 3 4 5 6 7 8 9 10 Требование 11 12
Накопление, ←Реализация требований ←—— →
отклоняется
систематизация и →
Проверка реализации ← (b) Решение о требовании
группировка требований
Распростанение изменения начато→ принято
Решение о реализации
Извлечение требования
требований принято (c) → Сообщение поступило (a) →
Рис. 4.17.
руты, возникающие в связи с обработкой одного требования в ходе эксплуа-
тации программной системы. Для упрощения предполагается, что операци-
онные маршруты не зависят от накапливаемой информации. Это заведомо
огрубляющее предположение в том смысле, что принятие решений о требо-
вании фактически делается не только на основании первичных установок,
но и с использованием знаний о системе, пользователях, о текущем предста-
влении о приоритетах и предпочтениях. Можно считать, что подобные све-
дения используются в точках разветвления операционных маршрутов, но то,
как осуществляется такое использование, моделью не описывается.
Аналогично организации мини-цикла для трассировки требований, в мо-
дели периода эксплуатации началом обработки является поступление сооб-
щения о ходе эксплуатации системы, которое можно трактовать как содер-
жащее требования (контрольная точка (a)). Это событие может возникать в
любой момент периода сопровождения, т. е. обсуждаемая модель является
естественным продолжением модели с обработкой требования в мини-цикле
(см. § 4.4.3). Так как отобразить весь поток сообщений невозможно, рассма-
тривается операционный маршрут только одного сообщения, при этом по-
стулируется, что все сообщения обрабатываются подобно:
• поступление сообщения (контрольная точка (a));
234 ГЛАВА 4. ЖИЗНЕННЫЙ ЦИКЛ
ентировано прежде всего на задачи, где заказчику важнее форма, чем содер-
жание, и где успех проекта зависит в первую очередь от упаковки, и лишь во
вторую — от качества начинки.
Вопросы, которые затрагивались в настоящей главе, освещены в многочи-
сленных публикациях, посвященных технологии программирования. Боль-
шинство из них соответствуют скорее текущей конъюнктуре, чем сути про-
блемы. В качестве приятного исключения, как классическую работу, выдер-
жавшую испытание времением, можно указать на книгу Ф. Брукса “The Mythical
Man-Month. Essay on Software Engineering” (русский перевод первого изда-
ния, вышедшего в 1975г., см. в [14], юбилейного издания 1995г. — в [15]). Эта
монография по праву считается одной из лучших книг не только по данной
тематике, но и по программированию вообще. Сопоставление двух ее изда-
ний явно показывает, что проблемы, которые приходится решать при упра-
влении программными проектами, почти не изменились со времени перфо-
карт. Меняется только техническая поддержка.
Из ранних работ, не потерявших своей актуальности, прежде всего сле-
дует обратить внимание на монографию Гантера [23], содержащую, кроме
представленной выше модели, много полезной информации для организа-
ции работ над программными проектами. Систематизированные сведения о
понятии жизненного цикла и его применении в промышленном программи-
ровании можно найти в книге [52], которая к тому же дает представление о
состоянии дел в этой области в СССР к началу восьмидесятых годов. Весь-
ма обстоятельное исследование задач и методов проектирования и разработ-
ки программного обеспечения выполнено Боэмом. Его книга [11] постоянно
цитируется и в наши дни.
Современное представление о технологии проектирования программных
систем прочно связано с методологией объектно-ориентированного програм-
мирования. Всестороннее изложение данного подхода, его концепций, а так-
же общих методов разработки проектов в объектно-ориентированном стиле
можно найти в книге Буча [16]. UML и методы его использования в практи-
ческой разработке программных проектов хорошо изложены авторами это-
го языка в монографии [17]. Понятия, связанные с CASE-технологиями, до-
статочно четко излагаются в работах [64, 18]. В частности, в последней из
упомянутых публикаций достаточно подробно освещаются вопросы CASE-
технологий, связанных проектированием информационных систем.
Следующие ссылки помогут получить сведения об упомянутых выше кон-
кретных разработках. Книга [70] дает наиболее полное представление о СУБД
Oracle, в частности, об Oracle Designer 2000 и его месте в системе. IDEF-тех-
242 ГЛАВА 4. ЖИЗНЕННЫЙ ЦИКЛ
Структуры программирования
244
245
Выражения
247
248 ГЛАВА 5. ВЫРАЖЕНИЯ
§ 5.1. ОПЕРАЦИИ
формы.
АЛМО — русская попытка (1967 г.) создать то, чем затем стал язык C: язык высокого
уровня, максимально приближенный к архитектуре машины. АЛМО до середины 80-х гг.
4
x := a/b;
(5.3)
x := a div b;
где x, a, b — целые переменные, могут быть различны.
Аналогичный эффект можно получить и в языке С, но здесь всплывает в
явном виде еще одно важное понятие современных языков.
x=float(a)/float(b);
(5.4)
x=a/b;