Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование. Процедурное программирование. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
Основные возможности процедурного программирования по работе с числовыми данными
)
Процедурные типы
Процедурный тип является важнейшим типом современного про-
граммирования. Он описывает совокупности процедур или функций, имеющих однотипные заголовки. Для объявления процедурного типа (в разделе 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 : Integ­er); 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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]