Имитация работы цикла с помощью рекурсии



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

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

Пример 1.

procedure LoopImitation(i, n: integer); {Первый параметр – счетчик шагов, второй параметр – общее количество шагов} begin writeln('Hello N ', i); //Здесь любые инструкции, которые будут повторятся if i<=n then //Пока счетчик цикла не станет равным максимальному LoopImitation(i+1, n); //значению n, повторяем инструкции путем вызова нового экземпляра процедуры end;

Результатом вызова вида LoopImitation(1, 10) станет десятикратное выполнение инструкций с изменением счетчика от 1 до 10. В данном случае будет напечатано:

Hello N 1

Hello N 2

Hello N 10

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

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

Пример 2.

procedure LoopImitation2(i, n: integer); begin if i<=n then LoopImitation2(i+1, n); writeln('Hello N ', i); end;

В этом случае, прежде чем начнут выполняться инструкции, произойдет рекурсивный вызов процедуры. Новый экземпляр процедуры также, прежде всего, вызовет еще один экземпляр и так далее, пока не дойдем до максимального значения счетчика. Только после этого последняя из вызванных процедур выполнит свои инструкции, затем выполнит свои инструкции предпоследняя и т.д. Результатом вызова LoopImitation2(1, 10) будет печать приветствий в обратном порядке:

Hello N 10

Hello N 1

Если представить себе цепочку из рекурсивно вызванных процедур, то в примере 1 мы проходим ее от раньше вызванных процедур к более поздним. В примере 2 наоборот от более поздних к ранним.

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

procedure LoopImitation3(i, n: integer); begin writeln('Hello N ', i); {Здесь может располагаться первый блок инструкций} if i<=n then LoopImitation3(i+1, n); writeln('Hello N ', i); {Здесь может располагаться второй блок инструкций} end;

Здесь сначала последовательно выполнятся инструкции из первого блока затем в обратном порядке инструкции второго блока. При вызове LoopImitation3(1, 10) получим:

Hello N 1

Hello N 10

Hello N 10

Hello N 1

Потребуется сразу два цикла, чтобы сделать то же самое без рекурсии.

Рекурсия и рекуррентные соотношения

Простым примером величины, вычисляемой с помощью рекуррентных соотношений, является факториал

Очередной факториал можно вычислить по предыдущему как:

Введя обозначение , получим соотношение:

Тогда вычисление требуемого элемента последовательности будет состоять в повторяющемся обновлении их значений. В частности для факториала:

1 2 3 4 x := 1; for i := 2 to n do x := x * i; writeln(x);

Каждое такое обновление (x := x * i) называется итерацией, а процесс повторения итераций –итерированием.

Обратим, однако, внимание, что соотношение (1) является чисто рекурсивным определением последовательности и вычисление n-го элемента есть на самом деле многократное взятие функции f от самой себя:

В частности для факториала можно написать:

1 2 3 4 5 6 7 function Factorial(n: integer): integer; begin if n > 1 then Factorial := n * Factorial(n-1) else Factorial := 1; end;

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


Дата добавления: 2022-01-22; просмотров: 21; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!