Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция 14-15_РЕкурсия.doc
Скачиваний:
23
Добавлен:
24.08.2019
Размер:
236.03 Кб
Скачать

Формы рекурсивных процедур

В общем случае любая рекурсивная процедура Rec включает в некоторое множество операторов S и один или несколько операторов рекурсивного вызова.

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

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

Если условие истинно, то рекурсивный спуск продолжается. Когда оно становится ложным, то спуск заканчивается и начинается поочередный рекурсивный возврат из всех вызванных на данный момент копий рекурсивной процедуры.

Структура рекурсивной процедуры может принимать три разных формы:

  1. Форма с выполнением действий до рекурсивного вызова (с выполнением действий на рекурсивном спуске):

procedure Rec;

begin

S; {операторы}

If <условие> then Rec;

end;

  1. Форма с выполнением действий после рекурсивного вызова (с выполнением действий на рекурсивном возврате):

procedure Rec;

begin

if <условие> then Rec;

S; {операторы}

end;

3. Форма с выполнением действий как до, так и после рекурсивного вызова (с выполнением действий как на рекурсивном спуске, так и на рекурсивном возврате):

procedure Rec;

begin

S1;

if <условие> then Rec;

S2;

end;

или

procedure Rec;

begin

if <условие> then

begin

S1;

Rec;

S2;

end;

end;

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

Первые две формы рекурсивных подпрограмм рассмотрим на примере вычисления факториала (n!), третью форму - на примере реверсивной печати вводимой строки.

Рекурсивный спуск

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

Mult - для выполнения до рекурсивного вызова (то есть на спуске) операции умножения накапливаемого значения факториала на очередной множитель;

m - для обеспечения независимости рекурсивной функции от имени конкретной глобальной переменной, то есть для повышения универсальности функции.

Программа Factorial_Down, которая использует рекурсивную функцию Fact_Dn, выполняющую вычисления на спуске, имеет такой вид:

program Factorial_Down;

var n : Integer;

function Fact_Dn(Mult : Longint; i, m : Integer ) :Longint;

begin

Mult := Mult * i;

{ Накопление факториала стоит до оператора рекурсивного вызова.}

{ Следовательно вычисление выполняется на спуске. }

if i = m then Fact_Dn := Mult

else Fact_Dn := Fact_Dn (Mult, i+1, m)

end;

begin

Write ('Введите число n: ');

Readln ( n );

Writeln ('Факториал n! = ' , Fact_Dn(1,1,n ));

end.

Для демонстрации выполняемых функцией Fact_Dn действий приведем таблицу трассировки значений ее параметров по уровням рекурсии. В этой таблице рассмотрен конкретный случай для n = 5.

Рис.1. Трассировка значений параметров

Рассмотренная выше программа Factorial, использующая рекурсивную функцию Fact, выполняет вычисление факториала на возврате. Но это не совсем очевидно, поскольку в функции Fact рекурсивный вызов и операция умножения совмещены в одном операторе присваивания. Для более понятной демонстрации работы на возврате, приведем программу Factorial_Up, использующую функцию Fact_Up, в которой рекурсивный вызов и оператор накопления факториала разделены явным образом.

program Factorial_Up;

var

n : Integer;

function Fact_Up(i :Integer) : Longint;

var

Mult: Longint;

begin

if i = 1 then Mult := 1

else Mult := Fact_Up (i-1);

Fact_Up := Mult * i {Накопление факториала стоит после }

{оператора рекурсивного вызова. }

{Следовательно вычисление выполняется на возврате. }

end;

begin

Write ( 'Введите число n: ');

Readln (n);

Writeln ('Факториал n! = ', Fact_Up (n));

end.

Приведем таблицу трассировки по уровням рекурсии, аналогичную таблице для функции Fact_Dn:

Рис.2. Трассировка значений параметров