Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Программирование. Процедурное программирование. Учебное пособие
.pdf
Основные возможности процедурного программирования по работе с числовыми данными
)
Процедурные типы
Процедурный тип является важнейшим типом современного про-
граммирования. Он описывает совокупности процедур или функций,
имеющих однотипные заголовки. Для объявления процедурного типа
(в разделе type) используется заголовок процедуры (функции), в котором
опускается ее имя. Существуют два вида процедурных типов: типпроцедура и тип-функция.
Примеры объявления процедурных типов
type
Proc1 = procedure (A: array of Integer; N: Integer);
Proc2 = procedure (var X, Y: Real);
Func1 = function(C: Char): Integer;
В этих описаниях имена формальных параметров несущественны.
Важны только их количество, порядок следования, типы, а также тип результата для функции.
В разделе описания переменных может быть объявлена переменная
процедурного типа, которая выступает именем процедуры (функции)
с указанными в описании типа параметрами.
Введенный процедурный тип может быть использован для указания
типа формальных параметров подпрограмм. В этом случае при вызове
подпрограммы, имеющей параметр процедурного типа, в качестве фактического параметра передается переменная процедурного типа или имя существующей процедуры (функции), заголовок которой соответствует заголовку формального параметра.
Рекуррентность и рекурсия
К функции (процедуре) можно обращаться тремя способами из тела:
основной программы;
другой функции (процедуры);
самой функции (процедуры).
В последнем случае функции (процедуры) вызывают сами себя и на-
зываются рекурсивными.
В математике существует понятие рекуррентной формулы, которое
тесно связано с понятием рекурсии. Например, следующее определение
факториала X! представлено рекуррентной формулой, которая в программе
может быть реализована рекурсивной функцией
X
ì
ï
ï
!
=
í
ï
ï
î
1для 1,
XX X
⋅- >
(
X
=
1! для 1.
41

Глава 1
Показательная функция xy (для целочисленного показателя степени)
является примером другой функции, вычисление которой легко проводится в рекурсивной форме:
ì
ï
ï
1для 0,
ï
ï
ï
yy
ï
xxx y
=⋅ >
í
ï
ï
ï
ï
ï
ï
î
-
1
y
x
y
1
для 0,
для 0.
=
y
<
Характерные особенности использования рекурсивной функции:
наличие одного или нескольких условий для выхода из рекурсии
1)
(условия завершения);
рекурсивный вызов функции с последовательным изменением
2)
значений одного или нескольких аргументов, которые обеспечивают сходимость к одному из условий завершения.
Пример программы № 5
Быстрая сортировка массива с использованием алгоритма Хоара.
Текст рекурсивной процедуры приведен в листинге 5.
Алгоритм быстрой сортировки массива основан на том факте, что
для достижения наибольшей эффективности желательно производить обмены на больших расстояниях.
Рассмотрим подробнее алгоритм быстрой сортировки массива.
Выберем средний элемент в массиве, назовем его X.
1.
Просмотрим массив с начала, пока не найдем первый элемент мас-
2.
сива, превосходящий X: A[I] > X.
Просмотрим массив с конца, пока не найдем первый с конца эле-
3.
мент массива, меньший X: A[J] < X.
Проверяем, находится ли I-й элемент левее J-го. Если это выпол-
4.
няется (I < J), то меняем их между собой и возвращаемся к пункту 2.
Если I > J, то массив разделился на две части. В левой части нахо-
5.
дятся элементы с ключами, меньшими (или равными) X, а в правой –
с ключами, большими (или равными) X.
Описанный алгоритм применяется к обеим полученным частям,
6.
а потом к частям этих частей, пока каждая часть не будет содержать один
элемент.
Рекурсивный вызов может быть косвенным. В этом случае подпро-
грамма обращается к себе опосредованно, через вызов другой подпрограммы, которая, в свою очередь, обращается к первой.
42

Основные возможности процедурного программирования по работе с числовыми данными
Листинг 5. Процедура быстрой сортировки массива
const
NMax = 100;
type
TVector = array[1..NMax] of Integer;
//Процедура быстрой сортировки массива
procedure QuickSort(var A : TVector; Left, Right, M : Integer);
var
I, J : Integer;
X, W : Integer;
begin
I := Left; J := Right;
X := A[(Left + Right) div 2];
repeat
while A[I] < X do I := I + 1;
while X < A[J] do J := J - 1;
if I < J then
begin
W := A[I]; A[I] := A[J]; A[J] := W;
I := I + 1; J := J - 1;
end;
until I > J;
if Left < J then QuickSort(A, Left, J, M);
if I < Right then QuickSort(A, I, Right, M);
end;
Согласно правилам языка Delphi каждый идентификатор перед упот-
реблением должен быть описан. Поэтому в случае взаимного обращения
подпрограмм друг к другу используется опережающее описание. Опережающее описание заключается в объявлении только заголовка подпрограммы с директивой
forward
. После такого описания можно использовать
обращение к подпрограмме.
Пример программы № 6
С клавиатуры вводится правильное арифметическое выражение,
заканчивающееся символом «точка». Требуется преобразовать выражение в постфиксную форму записи.
Постфиксная запись (польская инверсная запись) – бесскобочная за-
пись арифметических выражений, при которой знак операции ставится после операндов.
Вид арифметического выражения определяется следующим образом:
43

Глава 1
<выражение> ::= <слагаемое> | <слагаемое> <операция1>
<выражение>
<операция1> ::= + | <слагаемое> ::= <множитель> | <множитель> <операция2>
<множитель>
<операция2> ::= * | /
<множитель> ::= <идентификатор> | (<выражение>)
<идентификатор> ::= <буква>
Примеры преобразований:
(A + B)*(C – D) → AB+CD–*
A + B * C – D / E → ABC*+DE/–
(A + (B + C) * D) – E / F → ABC+D*EF/–
Приведенные синтаксические конструкции обрабатываются при по-
мощи взаимно рекурсивных процедур. Текст программы приведен в листинге 6.
Листинг 6. Преобразование арифметического выражения
в постфиксную форму
program Example6;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows;
// Процедура преобразования выражения в постфиксную форму
procedure Postfix;
var S : Char; // Очередной считываемый символ
// Процедура поиска лишних пробелов
procedure FindSymbol;
begin
{В цикле пропускаем все символы, пока не будет прочитан
символ, отличный от пробела}
repeat
Read(S);
until S <> ' ';
end;
// Опережающие описания:
procedure FindFactor; forward;
procedure FindExpression; forward;
// Обработка слагаемого
procedure FindTerm;
var Z : Char;
begin
FindFactor;
while (S = '*') or (S = '/') do
begin
Z := S; // Сохранение знака операции
FindSymbol; // Пропуск пробелов
FindFactor; // Обработка множителя
44

Основные возможности процедурного программирования по работе с числовыми данными
Write(Z); // Вывод знака операции
end;
end;
// Обработка множителя
procedure FindFactor;
begin
if S = '('
then
begin
FindSymbol; // Пропуск пробелов
FindExpression; // Обработка выражения
end
else
Write(S); // Вывод символа
FindSymbol; // Пропуск пробелов
end;
// Обработка арифметического выражения
procedure FindExpression;
var Z: Char;
begin
FindTerm; // Обработка первого слагаемого
while (S = '+') or (S = '-') do
begin
Z := S; // Сохранение знака операции
FindSymbol; // Пропуск пробелов
FindTerm; // Обработка последующего слагаемого
Write(Z); // Вывод знака операции
end;
end;
begin {Postfix}
FindSymbol; // Пропуск пробелов
Write('Постфиксное выражение: ');
repeat
FindExpression; // Обработка выражения
until S = '.';
Writeln;
end; {Postfix}
var Answer: Integer;
begin
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
repeat
Writeln('Введите арифметическое выражение: ');
Postfix;
Write('Для повтора введите 1, для выхода 0: ');
Read(Answer);
until Answer = 0;
end.
45

Глава 1
Рис. 4. Пример вывода программы по преобразованию
арифметического выражения
Пример работы программы приведен на рис. 4.
Контрольные вопросы и задания
1. В чем заключается назначение процедур и функций? Чем отлича-
ются функции от процедур?
В чем состоит особенность использования локальных перемен-
2.
ных? Если локальная и глобальная переменные имеют одинаковые имена,
то к какой переменной будет идти обращение из подпрограммы, из программы?
Что такое формальные и фактические параметры? Перечислите
3.
существующие виды формальных параметров. В каких случаях в качестве
фактического параметра может выступать переменная, выражение, константа? Могут ли имена фактических параметров совпадать с именами
формальных параметров?
Что такое рекурсия? Каковы основные требования к реализации
4.
рекурсивного алгоритма?
Что такое косвенная рекурсия? Каковы механизмы ее реализации?
5.
1.5. Математическая индукция, рекурсия и итерация.
Фракталы
Математическая индукция
Рекурсия тесно связана с понятием математической индукции. Ма-
тематическая индукция – метод рассуждения от частного к общему. В на-
46

Основные возможности процедурного программирования по работе с числовыми данными
стоящее время существует несколько видов математической индукции.
В простейшем случае принцип математической индукции состоит в следующем. Пусть P(n, n
доказать для любого аргумента n (n, n
, k) – утверждение, истинность которого необходимо
0
, k – положительные целые числа, n0 –
0
начальное значение, k – шаг перехода к следующему значению). В этом
случае доказательство разбивается на следующие шаги:
показывается истинность P(n0);
1)
предполагается истинность P(n – k);
2)
из двух предыдущих шагов выводится истинность утверждения P(n).
3)
Пример. Доказать, что n! > 2
n
для всех целых чисел n ≥ 4.
Доказательство:
Для n = 4 утверждение истинно, так как 4! = 24 > 16.
1.
Предположим, утверждение истинно для n – 1, т. е. (n – 1)! > 2
2.
Требуется показать, что n! > 2n. Действительно,
3.
n! = (n – 1)! · n > 2
n–1
· n > 4 · 2
n–1
> 2n.
n–1
.
Что и требовалось доказать.
Рекурсия сходна с индукцией, поскольку использует свои возможно-
сти по решению задач для данных большего размера, выводя окончательное решение из решения подобных задач меньшего размера.
Рекурсия и итерация
В большинстве случаев рекурсивную программу можно трансфор-
мировать в чисто итеративную и наоборот. Разворачивание рекурсии
в цикл применяется в целях оптимизации выполнения программного кода
по времени и/или памяти; более того, некоторые компиляторы уже сами
умеют разворачивать простейшие случаи рекурсии. Итеративный и рекурсивный методы программирования имеют свои достоинства и недостатки.
Выбор одного из этих подходов зависит от класса решаемой задачи.
Например, для задач, содержащих рекурсивную зависимость или данные
самоподобной структуры уже в своей формулировке, проще разработать
рекурсивный алгоритм, а затем, если потребуется, его оптимизировать.
Другим примером задач, для которых рекурсивный алгоритм является более подходящим, – это задачи поиска с возвратом (например, поиск выхода
из лабиринта и другие).
К достоинствам итеративного метода следует отнести, как уже отме-
чалось выше, более оптимальное использование ресурсов вычислительной
машины и, как следствие, время решения.
Рекурсивная форма организации алгоритма обычно выглядит изящ-
нее итеративной и дает более компактный исходный код, но выполняется
такая программа, как правило, медленнее и может вызвать переполнение
47

Глава 1
стека рекурсии (при каждом входе в подпрограмму ее локальные переменные размещаются в выделенном ограниченном участке памяти, так называемом стеке рекурсии).
Фракталы
Одной из областей применения рекурсивных алгоритмов является
построение фракталов. Фрактал – математическое множество, обладающее
свойством самоподобия (часть, подобная целому). Одним из видов фракталов являются геометрические фракталы. Геометрические фракталы получаются путем простых геометрических построений. При их построении
вначале берется базовая геометрическая фигура, на частях которой строятся по определенным правилам фигуры, подобные базовой меньшего размера. Далее процесс повторяется. С каждым шагом фигура становится все
сложнее и сложнее. В результате получается геометрический фрактал.
Пример программы № 7
Построить фрактал «Ковер Серпинского» с заданной стороной
квадрата.
Ковер Серпинского (квадрат Серпинского) – фрактал, предложенный
польским математиком Вацлавом Серпинским. Алгоритм его построения
следующий. Квадрат Q
равных квадратов. Из квадрата Q
делится прямыми, параллельными сторонам, на 9
0
удаляется центральный квадрат. Для ос-
0
тавшихся 8 квадратов все действия повторяются. Продолжая этот процесс
бесконечно, получается бесконечная последовательность:
QQ QÉÉÉ É
01 n
Пересечение ее членов есть ковер Серпинского.
В листинге 7 приведен текст программы построения фрактала «Ко-
вер Серпинского», в которой используется двумерный массив символов:
символ
означает, что точка принадлежит фракталу, символ
' '
(про-
'#'
бел) – точка фракталу не принадлежит.
Листинг 7. Построение фрактала «Ковер Серпинского»
program Example7;
{$APPTYPE CONSOLE}
uses SysUtils;
const N = 81;
type TMatrix = array[1..N,1..N] of Char;
procedure CreateSierpinskiCarpet(var Matrix: TMatrix;
N:Integer);
var
48

Основные возможности процедурного программирования по работе с числовыми данными
I, J: Integer;
procedure SierpinskiCarpet(I1,J1: Integer; I2,J2: Integer);
var
A, B, C, D: Integer;
I, J, Size: Integer;
X1, Y1, X2, Y2: Integer;
begin {procedure SierpinskiCarpet}
Size := (I2 - I1 + 1) div 3; // 1/3 от стороны квадрата
// Координаты центрального квадрата:
A := I1 + Size; B := I1 + 2*Size - 1;
C := J1 + Size; D := J1 + 2*Size - 1;
// Удаление центрального квадрата:
for I := A to B do
for J := C to D do
Matrix[i,j] := ' ';
if Size > 1 then
begin
for I := 1 to 3 do
for J := 1 to 3 do
if not ((I = 2) and (J = 2)) then
begin
// Левый верхний угол:
X1 := I1 + (I-1)*Size; Y1 := J1 + (J-1)*Size;
// Правый нижний угол:
X2 := I1 + I*Size - 1; Y2 := J1 + J*Size -1;
// Рекурсивный вызов:
SierpinskiCarpet(X1, Y1, X2, Y2);
end;
end;
end; {procedure SierpinskiCarpet}
begin {procedure CreateSierpinskiCarpet}
// Начальное заполнение матрицы:
for I := 1 to N do
for J := 1 to N do
Matrix[i,j]:='#';
SierpinskiCarpet(1,1, N, N);
end; {procedure CreateSierpinskiCarpet}
procedure PrintMatrix(Matrix: TMatrix; N: Integer);
var
I: Integer;
J: Integer;
begin
Writeln;
for I := 1 to N do
begin
write(' ');
for
J := 1 to N do Write(Matrix[i][j]:2);
49

Глава 1
WriteLn;
end;
end;
var
Matrix: TMatrix;
begin
CreateSierpinskiCarpet(Matrix, N);
PrintMatrix(Matrix, N);
ReadLn;
end.
На рис. 5 приведен результат работы программы.
Контрольные вопросы и задания
1. В чем сходство рекурсии и математический индукции?
В чем разница итеративных и рекурсивных алгоритмов?
2.
Назовите достоинства и недостатки итеративных, рекурсивных ал-
3.
горитмов.
Как применяется рекурсия для построения фракталов?
4.
Какие виды фракталов существуют?
5.
Рис. 5. Фрактал «Ковер Серпинского»
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
