Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ВЦ ОП.doc
Скачиваний:
7
Добавлен:
17.11.2019
Размер:
5.31 Mб
Скачать

19 Программирование алгоритмов с использованием рекурсивных подпрограмм

Цель работы

19.1.1 Составление программ с использованием рекурсивных подпрограмм - процедур, подпрограмм - функций

      1. Создание библиотек подпрограмм в Паскаль

      2. Отладка программы и контрольный просчет.

    1. Приборы и оборудование

19.2.1 Free Pascal

19.2.2 ПЭВМ IBM PC/XT

19.2.3 Microsoft Word

19.2.4 Задание на выполнение работы в электронном варианте.

19.3 ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ

19.3.1 Самостоятельно изучите электронный материал.

19.3.2 Перенесите предложенные программы, используя язык программирования Free Pascal

19.3.3 Запустите программу на выполнение

19.3.4 Результаты работы распечатайте на принтере и сделайте анализ.

19.3.5 Оформите самостоятельную работу для сдачи преподавателю

19.3.6 При оформлении лабораторной работы занесите задание и результат, а также вывод от полученного результата для сдачи преподавателю

19.4. Этапы работы

19.4.1 Рекурсия и опережающее описание

Рекурсия - это такой способ организации вычислительного процесса, при котором подпрограмма в ходе выполнения составляющих ее операторов обращается сама к себе.

Рассмотрим классический пример - вычисление факториала.

Программа вводит с клавиатуры целое число N и выводит на экран значение N!, которое вычисляется с помощью рекурсивной функции РАС.

Для выхода из программы необходимо либо ввести достаточно большое целое число, чтобы вызвать переполнение при умножении чисел с плавающей запятой, либо нажать Ctrl-Z и Enter.

При выполнении правильно организованной рекурсивной подпрограммы осуществляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи.

В примере решение при N = 0 тривиально и используется для остановки рекурсии.

Program Factorial;

{$S+} {Включаем контроль переполнения стека} 

var

n: Integer;

Function Facfn: Integer): Real; 

{Рекурсивная функция, вычисляющая n ! } 

begin {Fac}

 if n < 0 then

WriteLn ('Ошибка в задании N') 

else

if n = 0 then

Fac := 1

else Fac := n * Fac(n-l) 

end {Fac} ;

{---------------}

begin {main} repeat

 ReadLn (n) ; 

WriteLn ('n!= ',Fac(n)) 

until EOF 

end {main} .

Рекурсивная форма организации алгоритма обычно выглядит изящнее итерационной и дает более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать переполнение стека (при каждом входе в подпрограмму ее локальные переменные размещаются особым образом организованной области памяти, называемой программным стеком). Переполнение стека особенно ощутимо сказывается при работе с сопроцессором: если программа использует арифметический сопроцессор, результат любой вещественной функции возвращается через аппаратный стек сопроцессора, рассчитанный всего на 8 уровней.

Если, например, попытаться заменить тип REAL функции FAC на EXTENDED, программа перестанет работать уже при N = 8.

Чтобы избежать переполнения стека сопроцессора, следует размещать промежуточные результаты во вспомогательной переменной.

Вот правильный вариант примера для работы с типом EXTENDED:

Program Factorial;

{$S+,N+,E+} {Включаем контроль Стека и работу сопроцессора} 

var

n: Integer;

Function Fac(n: Integer): extended; 

var

F: extended; {Буферная переменная для разгрузки стека сопроцессора}

{Рекурсивная функция, вычисляющая п! } 

begin {Рас} 

if n < 0 then

WriteLn ('Ошибка в задании N') else

if n = 0 then

Fac := 1 else begin

F := Fac(n-l) ; Fac := F * n end end {Fac} ;

{--------------}

begin {main} 

repeat

ReadLn (n) ;

WriteLn ('n! = ',Fac(n)) 

until EOF 

end {main} .

Рекурсивный вызов может быть косвенным.

В этом случае подпрограмма обращается к себе опосредованно, путем вызова другой подпрограммы, в которой содержится обращение к первой, например:

Procedure A (i : Byte) ; 

begin

В (i);

.......

end ;

Procedure В (j : Byte) ;

begin

A(j);

end;

Если строго следовать правилу, согласно которому каждый идентификатор перед употреблением должен быть описан, то такую программную конструкцию использовать нельзя.

Для того, чтобы такого рода вызовы стали возможны, вводится опережающее описание:

Procedure В(j : Byte); forward;

Procedure A(i : Byte);

begin

.......

В (i) ;

.......

end ;

Procedure В; 

begin

.......

A(j);

.......

end;

Как видим, опережающее описание заключается в том, что объявляется лишь заголовок процедуры В, а ее тело заменяется стандартной директивой FORWARD.

Теперь в процедуре А можно использовать обращение к процедуре В - ведь она уже описана, точнее, известны ее формальные параметры, и компилятор может правильным образом организовать ее вызов.

Обратите внимание: тело процедуры

В начинается заголовком, в котором уже не указываются описанные ранее формальные параметры.