Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КОМП. НАУКИ_1сем.doc
Скачиваний:
6
Добавлен:
05.11.2018
Размер:
902.14 Кб
Скачать
    1. Прямая и косвенная рекурсия

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

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

      procedure P;

      begin  P  end;

      procedure P; begin  Q  end;

      procedure Q; begin  P  end;

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

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

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

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

procedure P;

begin … Q … end;

procedure Q; {Полное описание вызываемой процедуры Q, заголовок полного описания можно указывать в сокращенном виде, без списка формальных параметров}

begin … P … end;

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

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

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

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

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

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

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

program EndLess; {Бесконечная}

procedure PopAndDog;

begin

{*} writeln('У попа была собака, …');

{**}PopAndDog

end;

begin PopAndDog end.

Теоретически программа будет бесконечно выводить всем известные строки (вывод на рекурсивном спуске). Однако, если в процедуре PopAndDog поменять местами оператор вывода и оператор вызова PopAndDog, то она ничего не выведет, хотя теоретически будет работать бесконечно. Это объясняется тем, что оператор вызова новой копии процедуры PopAndDog будет стоять ДО оператора writeln. Вывод будет на рекурсивном возврате, который никогда не произойдет из-за того, что рекурсивный вызов происходит безусловно.

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

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

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

procedure PopAndDog(n:integer);

begin

writeln('У попа была собака, …');

if n>0 then PopAndDog(n-1)

end;

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