Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2011_12 Комп.Науки_2сем.doc
Скачиваний:
4
Добавлен:
13.09.2019
Размер:
476.67 Кб
Скачать

19.2.Рекурсивные подпрограммы

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

Пример рекурсивной функции, вычисляющей факториал:

function RF(n:integer):integer;

begin

if n=0 then RF:=1

else RF:=n*RF(n-1)

end;

Глубина рекурсии – максимальное число рекурсивных вызовов подпрограммы без возврата во время ее выполнения.

Текущий уровень рекурсии – число рекурсивных вызовов в каждый конкретный момент времени.

Рекурсивным спуском называется процесс рекурсивных вызовов подпрограммы.

Рекурсивным возвратом называется поочередный рекурсивный выход из всех вызванных на данный момент копий рекурсивной подпрограммы.

На рекурсивном спуске в функции RF ничего не вычисляется, эта функция рекурсивно вызывается с параметром на 1 меньше в ожидании того момента, когда значение параметра станет 0. Например, при n=3:

Как только параметр станет равным 0, новых вызовов не будет, рекурсивный спуск заканчивается. Начнется рекурсивный возврат, т.е. завершение работы вызванных на рекурсивном спуске функций. При этом происходит вычисление факториала.

19.3.Прямая и косвенная рекурсия

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

Если подпрограмма P вызывает другую подпрограмму Q, которая, в свою очередь, прямо или косвенно вызывает Р, то Р называют косвенно рекурсивной.

procedure P;

begin  P  end;

procedure P; begin  Q  end;

procedure Q; begin  P  end;

19.4.Предварительное (опережающее) описание подпрограммы

При косвенной рекурсии две подпрограммы, например, P и Q, содержат взаимные вызовы друг друга. При компиляции процедуры P компилятор не может правильно обработать вызов процедуры Q, поскольку она описана ниже по тексту. Разрешить эту ситуацию позволяет предварительное (или опережающее) описание.

Предварительное описание – это заголовок подпрограммы, за которым следует ключевое слово forward вместо тела подпрограммы. Предварительное описание вызываемой процедуры Q дается раньше описания вызывающей процедуры P

procedure Q(список формальных параметров); forward; {Предварительное описание}

procedure P;

begin

… Q …

end;

procedure Q(список формальных параметров); {Полное описание вызываемой процедуры Q}

begin

… P …

end;

19.5.Опасности рекурсии

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

  1. Бесконечной рекурсии.

  2. Необоснованного применения рекурсии. Обычно это происходит, если алгоритм типа рекурсивного вычисления чисел Фибоначчи многократно вычисляет одни и те же промежуточные значения.

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

19.5.1.Бесконечная рекурсия

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

program Recur;

procedure Rec;

begin

{*} writeln(’Рекурсия');

{**}Rec

end;

begin Rec end.

Теоретически программа будет бесконечно выводить строку ’Рекурсия' (вывод на рекурсивном спуске). Однако, если в процедуре Rec поменять местами оператор {*} вывода и оператор {**} вызова Rec, то она ничего не выведет, хотя теоретически будет работать бесконечно. Вывод должен выполняться на рекурсивном возврате, который никогда не произойдет из-за того, что рекурсивный вызов происходит безусловно.

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

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

Добавим в процедуру Rec параметр, который сначала равен максимальному числу рекурсивных вызовов, а при очередном рекурсивном вызове уменьшается на 1.

procedure Pec(n:integer);

begin

writeln(’Рекурсия');

if n>0 then Rec(n-1)

end;

После n вызовов значение n станет равным 0, и условие вызова перестанет выполняться.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]