|
Структурное программирование
Построение программы из
простых и составных операторов
С появлением ПК в начале 70 годов была осознана
необходимость разработки массовых технологий
программирования. Структурное
программирование - это один из ответов на
потребности практики. Согласно этому подходу
программа пишут так, что ее можно прочитать
неотрывно, как письмо. Исключается необходимость
перескакивать с одного места в другое место
программы при помощи операторов goto.
Структурированная программа не только легче
читается, но и легче воспринимается другими,
легче отлаживается и сопровождается. Впервые
технология была опробована в Паскале, а затем
стандарты Ф-77, Ф-90 и Ф-95 ввели в язык Фортран
адекватные средства
программирования. В ряду других подходов
можно назвать объектно-ориентированное
(управляемое данными) программирование
и визуальное программирование, основанное
на использовании планшета для создания форм,
ориентированных на пользователя и сопутствующих
программных модулей, дополняемых программистом.
Программа состоит из программных единиц (ПЕ),
следующих друг за другом. Простейшая программа
состоит из единственной ПЕ - главной программы.
ПЕ служит внешней оболочкой, куда вкладывают
неисполняемые а затем исполняемые операторы.
Исполняемые операторы записывают в порядке,
определяемом алгоритмом работы ПЕ. Для
каждой ПЕ алгоритм изображают в виде
отдельной блок-схемы.
Блок-схемы алгоритмов и программ
Алгоритм - это последовательность
действий, приводящая к результату. Действия
сопоставляются исполняемым операторам
программы. Блок-схема программы помогает
расставить исполняемые операторы в соответствии
с алгоритмом. Блок-схема строится из
ограниченного набора блоков, указанных в
таблице, а также линий, соединяющих их в порядке
выполнения. Блок-схема - это способ рассуждения
об алгоритме на человеческом языке. Запись
алгоритма в виде программы - это формальное
компьютерное описание алгоритма. Способов
обсуждения может быть много, но графические
образы существенно облегчают восприятие сложных
структур человеком. Техника рисования
упрощается при использовании компьютерных
средств, в частности, пакета MS VISIO или SMART DRAW 5. Эти
пакеты позволяют рисовать схемы из готовых
блоков, в-частности он позволяет рисовать
блок-схемы. Блоки легко масштабируются,
дополняются надписями, соединяются линиями. При
перемещении блоков линии переезжают вслед за
блоками.
БЛОКИ |
ПОЯСНЕНИЯ |
|
начало и конец
ПЕ
на каждую ПЕ - своя блок-схема |
соединительные
линии для блоков
влево и вверх - со стрелкками |
линии также соответствуют
операторам exit
cycle
goto m |
|
эти простые
операторы, они имеют
один вход и один выход |
структурный if как блок |
if, имея
внутри разветвления, все равно как блок
имееет один вход и один выход Переключатель структурный - это обобщение
структурного if на случай любого
количества блоков (в таблице не показан)
как блок имееет один вход и один выход
условный if - это частный случай
структурного if, в котором блок
"нет" пуст
Логический if-
это частный случай условного if,
в котором вместо блока - простой оператор
|
условный if как блок |
цикл как блок |
имееет один вход и один
выход необязательный дополнительный выход из
цикла передает управление в ту же точку, что и
нормальный выход |
пара блоков как новый блок |
следование блоков: соединить блоки
последовательно - конец одного с началом
следующего |
Все перечисленные конструкции
имеют единственный вход в блок и обязательный
конец блока. Поэтому эти блоки могут служить
строительными кирпичиками, из которых строится
структурированная программа. Досрочный выход
также переходит в конец блока.
Сначала рисуют не подробную блок-схему,
начало
конец
а потом ее детализируют.
При создании алгоритма блок-схему
компонуют из блоков, применяя всего-навсего один
прием - детализировать содержимое блока в виде
одной из перечисленных в таблице картинок.
Начало детализирующей схемы соединяют с началом
детализируемого блока, а конец детализирующей
схемы с концом детализируемого блока. Картинка
как бы разбухает.
начало
конец
Важно отметить,
- что все, даже самые сложные алгоритмы,
строятся именно так
- что в подставляемых картинках нет
большего разнообразия - они берутся только из
таблицы
- что сложность возникает не из-за все
большего разнообразия подставляемых картинок, а
из-за большего количества подстановок
- новое качество - сложность -
нарастает по мере увеличения количества
подстановок
По готовой блок-схеме легко писать
программу.
По готовой программе почти формальным
путем можно нарисовать блок-схему, которая в этом
случае служит документом дпя лучшего понимания
программы.
Имеются программы рисования
придумываемых блок-схем, например VISIO.
Имеются также программы печати
блок-схем по готовым программам.
Программу строят из составных операторов
(также СО называют конструкциями) и простых
операторов, руководствуясь следующим
- ПЕ пишут последовательно внутри одного текста
или в разных текстах, объединяя их в один проект
- в ПЕ вкладывают другие операторы, сначала
неисполняемые, а затем исполняемые операторы
- последовательно пишут любые операторы
- простые исполняемые операторы вкладывают в ПЕ
или блоки
- блок - это начинка составного исполняемого
оператора
- составной исполняемый оператор содержит один,
два, либо несколько блоков
- границы блока - это ключевые строки -
таковы традиции Фортрана, например
IF () then
.. .. блок ДА
else
.. .. блок НЕТ
end IF
- составные исполняемые операторы вкладывают в
качестве блоков без ограничений на глубину
|
БЛОК |
__простой __ |
следование |
|
__простой __
__простой __
__простой __ |
вложение в блок |
|
|
- в неисполняемых СО, например, при описании
производных типов допускается не вложение, а
ссылки на ранее сделанные определения
Классификация
операторов управления ходом вычислений
Операторы управления ходом
вычислений позволяют изменить естественный
ход вычислений
- участок программы на выбор
- участок программы неоднократно
- досрочный выход из конструкции
Классификация производится в
зависимомости
- от типа управляющего выражения (строки
таблицы)
- числа подчиненных блоков (столбцы
таблицы)
- местоположение блока показано .. ..
Тип выражения |
ЧИСЛО подчиненных БЛОКОВ |
|
0-без блоков |
1 блок |
2 и более блоков |
без
выражения |
Безусловный
переход
goto mexit cycle return stop |
do ! бесконечный
цикл
.. ..
enddo |
|
логическое
выражение
(условие)
logical L |
Логический
if
if ( L ) простой
если L=.true. то выполнить простой
оператор |
Условный
if ( L ) then
.. ..
end if |
Структурный
if ( L ) then
.. ..
else
.. ..
end if |
итеративный
цикл
do while(L)
. ..
end do |
|
Векторный
условный
where (vectorL)
.. .. Векторное присваивание
endwhere |
Векторный
структурный
where (vectorL)
.. .. Векторное присваивание
elsewhere
.. .. Векторное присваивание
end where |
арифметическое
выражение
real R
арифметическое
выражение
integer I |
Переключатель
вычисляемый
goto ( ..m ), I Арифметический if
if(RI) m<,m=,m> |
Цикл
по параметру RI
do RI=RIн,RIк,RIш
.. ..
end doRI- вещественная или целая
переменная |
|
Переключатель структурный
select case( IC )
case ( ..constIC )
.. ..
..... .....
casedefault(..constIC)
.. ..
end select с любым числом
блоков |
символьная переменная - строка
из одного символа
character C
IC - целая или символьная
переменная |
Нерекомендуемые операторы - это
опасные в применении операторы с метками,
характерные для программ до новой эры
структурного программирования
- Безусловный
переход по
метке goto m
Переключатель вычисляемый
с метками goto ( ..m ), I
Арифметический оператор с метками
if( RI) m<,m=,m>
дополнительный возврат из
подпрограммы на метку return m
DO - Оператор цикла
Согласно классификации оператор цикла
- одноблочный оператор, управляющий ходом
вычислений. Оператор цикла - это исполняемый
составной оператор или блок. Как для составного
блочного оператора для него действуют общие
правила
- нельзя входить в цикл сбоку, вход в цикл
единственный
- допускается вложение блоков и простых
операторов в цикл
- допускается вложение циклов в другие блоки,
в том числе и циклы
- не допускается перекрытие циклов между собой
и с другими блоками
- допускается следование
- допускается досрочный выход из цикла
В заголовке цикла присутствует слово do
- по-русски "делать". В Фортране-90
представлены три вида циклов:
- цикл по параметру или цикл с заранее известным числом
повторений
- итеративный цикл
- бесконечный цикл
Таблица. Сравнение трех видов циклов
Цикл |
С
заранее известным числом повторений |
Итеративный |
Бесконечный |
Форма
записи |
DO
i= iн , iк , is
.. ..
ENDDO |
DO WHILE(условие)
.. ..
ENDDO |
DO
.. ..
ENDDO |
Число повторений |
n = max( 0, int[ ( iк - iн + is ) / is ] )
int - целая часть, max-максимум |
заранее не известно
|
Потенциально
бесконечное
|
Нормальн.
выход |
через n
раз |
когда
перестанет выполняться условие |
Никогда |
Обозначение
цикла на блок-схеме |
|
|
|
Цикл с заранее известным
числом повторений
Отличительная особенность этого вида циклов
- наличие параметра цикла и ключевая фраза
i = iн , iк , is , содержащая триплет, т.е. тройку
чисел. Триплет задает способ изменения параметра
цикла по закону арифметической прогрессии, где
- i -параметр цикла, переменная
- каждое из чисел триплета iн , iк , is -
это константа, переменная или арифметическое
выражение, при этом
iн - начальное значение параметра цикла,
iк - конечное значение параметра цикла,
is - шаг изменения параметра цикла
Например, цикл do step=2,11,3
заставляет изменяться параметр step по следующему
закону 2,5,8,11
Допускаются варианты написания цикла
DO i = iн , iк , is ! Обычная
форма без имени
.. ..
ENDDO ! удобна при
копировании
имя: DO i = iн , iк ,
is ! Полная форма DO с именем цикла
.. ..
ENDDO имя ! имя в
начале и в конце
DO i = iн , iк !
Сокращенная форма без указания шага is
.. ..
ENDDO ! подразумевается is=1
DO m, i = iн
, iк, is ! устаревшая
форма с меткой
.. ..
m ENDDO ! с
меткой у последнего оператора
или устаревшая форма с меткой и пустышкой в
конце цикла - так писали раньше, например, в
старых учебниках
do m, i= iн , iк , is ! устаревшая форма
.. ..
m continue ! помечали последний оператор
в области действия цикла
Правила применения цикла с
заранее известным числом повторений
Оператор DO сам принудительно
изменяет i по закону арифметической
прогрессии: программисту внутри цикла изменять
i запрещается
- Число повторений цикла рассчитывается заранее
до цикла по формуле
n = max( 0, int[( iк-iн+is)/is] )
причем n всегда целое, n зависит от iн , iк , is
max(x,y)-большее из двух чисел, причем в нашем
применении max(0,y), y= int[( iк-iн+is)/is] max(0,y)=y при y>0,
max(0,y)=0 при y<0,
max(0,y)=0 при y=0
int[x/y]-целая часть частного, иногда, ее,
как на рисунке, обозначают ] x/y [
n вычисляется до цикла - см. подробную
блок-схему цикла
Примеры на расчет числа
повторений цикла от простого к сложному
do i=3,9,2
n= (9-3+2)/2 = 8/2 = 4
do i = 0.3,0.9,0.2
n= (0.9-0.3+0.2) / 0.2 = 0.8
/ 0.2 = 4
do i=9,3,-2
n= [3-9+(-2)] / (-2) = -8/(-2)
= 4
do i=3,10,2
n=int( (10-3+2)/2 )=int( 9./2. )=int( 4.5 )=4
do i=9,3,2
n=max(0, (3-9+2) /2 )=max(0,
(-4) /2 ) = max(0, -2 ) = 0
do i=10,3,2 n=max(0,
int[ (3-10+2)/2] )=max(0, int[(-7)/2]) =max(0, int[ -3.5] )=max(0, -3 )=0
Параметр цикла - это переменная
предпочтительнее целого типа integer ,
чтобы параметр изменялся точно в
интервале [ iн , iк ]
чтобы число повторений цикла
рассчитывалось точно,
при этом последнее значение i внутри
цикла i=iн+(n-1)is=iк
при выходе из цикла i=iк+is
Величины iн , iк , is , как и i, могут
быть не только целого типа integer, но и типа real
или double precision. Следует понимать, что
в этом случае
параметр i изменяется не точно, а
приближенно
хуже того, ошибка по формуле i=i+is постепенно накапливается и становится тем
заметнее, чем больше число повторений
ввиду накопления ошибок
"последнее значение i=iн+(n-1)is
" может уйти за интервал [ iн , iк ],
вываливаясь за цикл
цикл выполняется на один раз меньше, см. пример
- Величины iн , iк , is в отличие от i могут быть не
только переменными, но и константами или
арифметическими выражениями. Изменять переменные
iн , iк , is внутри цикла можно, но на n это
не повлияет, так как n вычисляется до цикла
Шаг изменения i -это is, не равный 0,
причем is>0 - для возрастающего i , is<0 - для
убывающего i; ну а когда пишут, опустив шаг i =
iн,iк подразумевается is=1. При нормальном
выходе из цикла i достигает значение i=iк+is,
продвинутое за конечное значение на шаг
Do .. .. enddo
следующий оператор выполнится
при i=iк+is
Досрочный выход из цикла - это оператор exit,
передающий управление на оператор, следующий за enddo.
do i = iн , iк
, is
.. ..
if( e ) EXIT ! досрочный
ВЫХОД из ЦИКЛА
.. XXX..
enddo
оператор, следующий за циклом. На
этотоператор попадают
при досрочном выходе из цикла
достигнутое i сохраняется
при нормальном завершении цикла
i=iк+is
Именованная полная форма do
разрешает осуществлять досрочный выход по EXIT my с любой глубины
на оператор следующий за ENDDO my
my: DO
.. ..
do
.. ..
if( e) EXIT
my ! выход с любой глубины
.. XXX..
enddo
.. XXXXXX ..
ENDDO my
на оператор, следующий за
циклом my
Досрочный выход на метку m за циклом
производит и goto m, но его лучше
не применять!
- Следующий проход цикла CYCLE
do
.. ..
if( условие ) cycle ! к
следующему проходу цикла
..____ .. пропускаются
при выполнении cycle
enddo
Именованная полная форма do разрешает
использовать CYCLE my на любой
глубине
my: DO
.. ..
do
.. ..
if( e) CYCLE my ! с
любой глубины
..___. .
enddo ! ..___ ..
..___..
ENDDO my ! Следующий проход цикла
my
- В конструкции ( ..v , i= iн , iк , is ) или ( ..e
, i= iн , iк , is ), называемой неявным
do; вместо do .. .. enddo область
действия цикла ограничена скобками, а под ..v
подразумевается список переменных или ..e
- выражений, перечисленных через запятую и
зависящих от i . Неявный do используется
как элемент списка ввода-вывода в операторах read
/ write , а также в списках инициализации
оператора data и в конструкторе массива.
Проиллюстрируем применение неявного Do на
примере - вывода таблицы из 100 значений
замечательного предела
real x
write(2,11 ) &
( x,
sin(x)/x, x = 1,0.01,-0.01 )
11 format"('| x | sin(x)/x |'/ &
('|',e10.3, '|', e10.3,'|') )"
Рекомендации:
- в подстроках Фортрана-77, а затем и в
операторе Select case стало модно использовать
диапазоныи. В секциях массива пошли дальше и
стали использовать триплеты, по смыслу такие же,
как в циклах. Отличие в знаке препинания: вместо запятой-двоеточие.
Секции массивов, основанные на использовании
триплетов, - конструкция более емкая, чем неявный do,
например, сравни списки read и write при
вводе-выводе первой половины массива из 100
элементов
real A(1:100)
read(1,*) A
! ввести весь массив
read(1,*) (A(i),i=1,100) ! можно в более общем виде
write(1,*) (A(i),i=1,50) ! вывести только первую
половину массива
write(2,*) A(1:50) !
вывести только первую половину массива
write(2,*) A(1:100:2) ! вывести половину
элементов с нечетными номерами
write(2,*) A(1:99:2) ! вывести половину
элементов с нечетными номерами
write(2,*) A(2:100:2) ! вывести половину элементов с
четными номерами
- при большой глубине вложения блоков и
человек плохо воспринимает программу, и
диагностика затруднена. Заботясь о
читаемости программы, пользуйтесь именами
циклов, отступами и комментариями,
облегчающими восприятие, как в примере:
BIG:Do A= 1,10 ! BIG-БОЛЬШОЙ ЦИКЛ
по A
small:do x=0.7 ,
3.5, 0.35 ! small-малый цикл по x
write(3,*) x, a*x +7.2
end do small ! конец small
End Do BIG ! КОНЕЦ BIG
- внимание - в случае ошибок в закрытии
блоков компилятор иногда путает IF и DO в
неименованных конструкциях
В С и Паскале имеются аналогичные виды
циклов, правда, с некоторыми отличиями
- в цикле с заранее известным числом повторений в
С можно иметь несколько параллельно
изменяющихся параметров, каждый параметр
изменяется по-своему
- в цикле с заранее известным числом повторений в
Паскале допускается только целочисленный
параметр, изменяющийся с шагом 1 или -1
- в Паскале и в С используется ключевое слово FOR
вместо DO
- правописание оператора цикла в каждом языке
свое
- в Паскале и в С вместо фортрановского DO WHILE с
предусловием имеется две разновидности
итеративного цикла с предусловием и
постусловием
Примеры на вычисление
координат точек четверушки окружности во втором
квадранте
Даются все более удачные решения одной и той же
задачи с радиусом r=4.5, различающиеся выбором
угловой меры и типа параметра цикла, с
последующим сравнением полученных таблиц.
Названия решений DR, RR, RI.. состоят из двух букв,
причем
- первая буква указывает на то в чем задается угол
и шаг r-радианы D-градусы
- вторая буква указывает на то какого типа
параметра цикла R-вешественый, I-целый
Если адресовать полученную таблицу для aGRAPHER, то
первым действием должно быть open(2,file='okr2.dat')
- открыть файл с расширением *.dat
Решения варьируются в зависимости от применения
явного и неявного цикла и форматного вывода
- (Rr) самое плохое решение
с "потерей" последней точки pi или "лишней" точкой
после pi
угол в радианах, шаг 0.1 - не кратен pi, цикл DO - явный
program rr
real :: r=4.5, x,pi
integer i
open(1,file='okr2.txt')
pi=2.*asin(1.); i=0 ;
write(1,*) 'n=',int((pi-pi/2+0.1)/0.1)
! n=int[(pi-pi/2+0.1)/0.1]=int[(1.57+0.1)/0.1]=int[1.67*10]=int[16.7]=16,
write(1,*) " i al
x y"
do ral=pi/2,pi,0.1
i=i+1; x = r*cos(ral); y = r*sin(ral) ; write(1,*) i,ral, x,y
enddo
end
при этом получим значения
n= 16
i al x
y
1) 1.570796 -1.967013E-07 4.500000
... ...
16) 3.070796 -4.488727 3.183210E-0 --> недолёт
еще одну 17-ю точку на оси абсцисс можно
"добавить", искусственно продвинув ral на полшага и писать DO
так
do ral = pi/2, pi+0.05, 0.1
всего лишь, чтобы добавить строку
17) x= -4.498081 y=-1.313939E-01
--> перелёт, а не точно x=4.5;
y=0.
- (Rr) решение с явным DO, угол в радианах,
шаг в долях pi
real :: r-4.5, x,y,pi; real ral
! угол в радианах
pi=2.*asin(1.)
do ral=pi/2,pi,pi/30
x=r*cos(ral); y=r*sin(ral); write(2,*) x,y
enddo ! n=(pi-pi/2+pi/30)/(pi/30)=16
i al
x
y
1) 1.570796 -1.967013E-07 4.500000
.. ..
16) 3.141592 -4.500000 4.971016E-06 - значения не круглые, но
близкие
-
- (Dr) угол
вещественный в градусах
write размещен внутри явного do
real :: r=4.5 ,
dalf,x,y; real dalf !
угол в градусах
do dalf=90,180,2 ! угол в
градусах с шагом 2 градуса
x=r*cosd(dalf);
y=r*sind(dalf) ; write(2,*)
x,y
enddo ! -
количество точек n=(180-90+2)/2=46 и
все значения "кругленькие"
- (Dr) угол вещественный в градусах, вывод форматный
решение с неявным do,
размещенным внутри write
real r/4.5/ , dalf ! угол в
градусах с шагом 2 градуса
write(2,17)
(r*cosD(dalf),r*sinD(dalf), dalf=90.,180.,2.)
17 format( f8.3,
f8.3)
решение отличается лишь заменой явного цикла
на неявный и более аккуратным оформлением
результатов
.000 4.500
...
-4.500 .000
do размещен внутри write
- (Ri) решение с с целым параметром
цикла i=0,15 который легко пересчитывается в ral -
угол в радианах по формуле ral= pi/2 +i* pi/30
integer i; real
r/4.5/,x, y, pi ; real ral ! угол в
радианах
pi=2.*asin(1.)
do i=0,15
ral=pi/2 + i*pi/30; x=r*cos(ral); y=r*sin(ral) ;
write(2,"(f8.3,f8.3)") x,y
enddo ! число точек n=(15-0+1)/1 = 16 , а
значения
1) x=.000 y=4.500
....
16) x=-4.500 y=0.000 - приемлемые x,y
- Если сравнивать решения, делая шаг все мельче,
то именно последний вариант (RI) наиболее
защищен от ошибок, так как целое i изменяется в
цикле точно. Во всех других вариантах ошибка по
углу к концу интервала накапливается, тем
больше чем мельче шаг. Возможна еще одна
реализация ( DI ) также с циклом по целому
параметру ialf, который
переводится в вещественный аргумент при помощи
функции real(ialf)
- ( Di ) лучшее решение без накопления
ошибок с неявным DO, угол и шаг целые, в
градусах, вывод форматный
real r/4.5/; integer
ialf ! угол в градусах с шагом 2
градуса
write(2,"(f8.3,f8.3)") (r*cosD(real(ialf)),r*sinD(real(ialf)),ialf=90,180,2)
do размещен внутри write
Примечания к примерам:
- в Ф-90 функции sinD(degree) и cosd(degree) есть в числе
стандартных , degree - угол в градусах
- в Ф-77 функцию sinD(degree) можно определить так
real pi, degree
parameter (
pi=3.1415298)sinD(degree)=sin(degree*pi/180) ! аналогично
и косинус, degree - угол в градусах
|
. |