Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
5 Подпрограммы.doc
Скачиваний:
25
Добавлен:
09.02.2015
Размер:
369.15 Кб
Скачать

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

Язык Object Pascal допускает создание и использование рекурсивных подпрограмм. Это означает, что внутри подпрограммы можно обращаться к самой подпрограмме. Однако, чтобы такое обращение имело смысл, подпрограмма должна быть организована должным образом, т.е. реализовала бы именно рекурсивный алгоритм. Например, составим функцию, вычисляющую сумму элементов одномерного массива. Если использовать известный прием накопления суммы, предусматривающий вычисление суммы в цикле, то такая функция может иметь следующий вид:

  1. program Fun2rec;

  2. {$APPTYPE CONSOLE}

  3. uses

  4. SysUtils;

  5. const

  6. nn=20;

  7. type

  8. mas=array[1..nn] of Real;

  9. var

  10. a:mas;

  11. i,n:Integer;

  12. s:Real;

  13. function Sum(a:mas;n:Integer):Real;

  14. var i:Integer;

  15. begin

  16. Result:=0;

  17. for i:=1 to n do

  18. Result:=Result+a[i];

  19. end;

  20. begin// РАЗДЕЛ ОПЕРАТОРОВ ПРОГРАММЫ

  21. ReadLn(n);

  22. for i:=1 to n do

  23. Read(a[i]);

  24. ReadLn;

  25. s:=sum(a,n);

  26. WriteLn('s= ',s:6:1);

  27. ReadLn;

  28. end.

Внутри функции для накопления суммы использовалась внутренняя переменная Result. Использование внутри цикла оператора присваиванияSum:=Sum+a[i]привело бы к ошибке, так как появление в правой части оператора присваивания имени функции означало бы обращение к этой функции. Поскольку после имени функции отсутствуют параметры, то это означало бы синтаксическую ошибку. Но поскольку внутри функции реализован циклический алгоритм, а не рекурсивный, то операторSum:=Sum(a,n)+a[i]приводил бы также к ошибке (подпрограмма зацикливается).

Для построения рекурсивного варианта подпрограммы следует сформулировать рекурсивное утверждение и условие окончания рекурсии. В нашем случае рекурсивное утверждение будет заключаться в том, что сумма элементов массива из nэлементов может быть вычислена как сумма (n-1)-го начальных элементов и последнего элемента, то естьSn =Sn-1 +an(n>1). Условие окончания рекурсииS1 =a1. Рекурсивный вариант функции приведен ниже.

  1. function Sum(a:mas;n:Integer):Real;

  2. begin

  3. if n=1 then

  4. Sum:=a[n]

  5. else

  6. Sum:=a[n]+Sum(a,n-1)

  7. end;

Рекурсивное описание обычно является более компактным и выглядит более наглядно, если природа самого алгоритма рекурсивна. Однако надо принимать во внимание и то, как реализуется рекурсивный алгоритм в ЭВМ. Например, при n=4 (массив состоит из четырех элементов) производится первое обращение к функции со значениями параметров массиваaиn=4. При этих значениях выполняется операторSum:=a[4]+Sum(a,3).Затем выполняется обращение к функции со значениямиaи 3, т.е.sum(a,3). Затем выполняется обращение к функции со значениямиaи 2, т.е.Sum(a,2). При выполнении этого обращения к функции производится еще одно обращение:sum(a,1). На последнем шаге при обращении к функции с этими параметрами будет выполнен операторSum:=a[1]. Далее процесс развивается в обратном направлении, т.е. выполнится операторSum:=a[2]+Sum(a,1), что даст результатSum:=a[2]+a[1], затем выполнится операторSum:=a[3]+Sum(a,2), это дает результатSum:=a[3]+a[2]+a[1]. На последнем шаге выполнится операторSum:=a[4]+Sum(a,3), дающий окончательный результат. Рассмотрев описанный процесс, можно придти к выводу, что рекурсивная подпрограмма требует больших затрат машинного времени и памяти. Это объясняется необходимостью выполнения повторных обращений к подпрограмме и необходимостью хранения (в стеке) фактических параметров при обращениях к подпрограмме.

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

  1. procedure Pr1(a:Real;var b:Real);

  2. begin

  3. . . . . .

  4. Pr2(a,b);

  5. . . . . .

  6. end;

  7. procedure Pr2(c:Real;var d:Real);

  8. begin

  9. . . . . .

  10. Pr1(c,d);

  11. . . . . .

  12. end;

Согласно правилам языка Object Pascal каждый идентификатор должен быть предварительно объявлен, поэтому такая конструкция недопустима. Чтобы разрешить использование косвенной рекурсии, в состав языка введено опережающее объявление, реализуемое директивой forward. Сначала необходимо объявлен заголовок одной из процедур, после которого поместить указанную директиву. После этого следует расположить вторую процедуру, а затем разместить первую процедуру, в заголовке которой можно опустить список формальных параметров. Таким образом, во второй процедуре можно обращаться к первой процедуре, так как к этому моменту ее заголовок уже известен второй процедуре.

  1. procedure Pr1(a:Real;var b:Real); forward;

  2. procedure Pr2(c:Real;var d:Real);

  3. begin

  4. . . . . .

  5. Pr1(c,d);

  6. . . . . .

  7. end;

  8. procedure Pr1(a:Real;var b:Real);

  9. begin

  10. . . . . .

  11. Pr2(a,b);

  12. . . . . .

  13. end;

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