Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Процедуры и функции.doc
Скачиваний:
3
Добавлен:
08.12.2018
Размер:
522.24 Кб
Скачать

7.3.5. Рекурсии

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

Примером программы с использованием рекурсии может быть программа вы­числения факториала числа. (Факториалом натурального числа п называют произ­ведение чисел 1*2*...*п.)

После запуска программы на экран выводится запрос "Введите число N >", за­тем с клавиатуры считывается значение целого числа N и в выражении F:=Fakt(N) вызывается функция Fakt с параметром—значением N. В подпрограмме-функции вычисления факториала проверяется условие N=1. Если оно выполняется, то функ­ции Fakt присваивается значение 1, на этом выполнение подпрограммы-функции завершается. Если условие N=1 не соблюдается, то выполняется вычисление произ­ведения N*Fakt(N—1). Вычисление произведения носит рекурсивный характер, так как при этом осуществляется вызов функции Fakt(N—1), значение которой вычис­ляется, в свою очередь, через вызов функции Fakt, параметром которой также бу­дет функция Fakt, и т. д., до тех пор пока значение формального параметра N=1. Так как базовая часть описания рекурсивной функции Fakt определяет значение Fakt для N=1-, равным единице, то рекурсивные вызовы функции Fakt больше не выполняются, а наоборот, выполняется вычисление функции Fakt для чисел, воз­растающих от 1 до N, причем функция Fakt всякий раз возвращает значение, равное произведению очередного k-ro числа на факториал от k—1-го числа. Последнее возвращение результата вычисления функции Fakt присвоит переменной F значе­ние произведения всех чисел от 1 до N, т. е. факториал числа N.

Итак, при выполнении рекурсивной подпрограммы осуществляется многократ­ный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи. В нашем примере решение при N=1 тривиально, т. е. Fakt=l. Затем осуществляется возврат на верхний уровень с последовательным вычислением значения функции Fakt.

Упражнение 7. Запустите интегрированную среду программирования. Введите текст программы Demo_Rekurs и запишите файл на диск под соответствующим именем, а затем откомпилируйте его. После того как компиляция выполнится успешно, задайте для просмот­ ра в окне отладчика переменные N, F. Установите видимыми одновременно окна редактора с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с захо­ дом в функцию и пронаблюдайте за изменением значения переменной N при рекурсивных вызовах функции Fakt. •

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

7.3.6. Нетрадиционное использование подпрограмм

В Турбо Паскале есть случаи нетрадиционного объявления подпрограмм, когда в объявлении процедуры содержится директива interrupt (прерывание), external (внешняя) или inline (встроенная) или вместо блока в объявлении процедуры или функции написано forward (опережающая).

Interrupt (прерывание). Объявление процедуры может содержать директиву interrupt перед блоком, и тогда процедура рассматривается как процедура преры­вания. Прерыванием называется временное прекращение процесса, вызванное со­бытием, внешним по отношению к этому процессу. Необходимость в процедурах прерывания возникает, когда программист решает определить собственные алго­ритмы реакции на прерывание операционной системы, отменив при этом стан­дартные реакции. Отметим, что процедуры прерывания нельзя вызывать с помо­щью операторов процедур и что каждая процедура прерывания должна задавать список параметров точно так, как это показано ниже:

Примечание. CS, IP, АХ, ВХ, СХ, DX, SI, DI, DS, ES, ВР — регистры процессора.

Внешние объявления (external). Внешние объявления позволяют связывать отдельно скомпилированные процедуры и функции, написанные на языке ассемб­лера. С помощью директивы {$L имя файла} внешнюю программу можно связать с программой или модулем, написанным на Паскале.

Приведем следующие примеры объявлений внешних процедур:

В тексте программы при объявлении внешних подпрограмм нужно задать ди­рективу компилятору $L, аргументом которой является имя OBJ-файла, содержа­щего код подключаемой подпрограммы, например: {$L BLOCK.OBJ}

Assembler. Assembler-объявление позволяет вам написать процедуру или эункцию на встроенном Ассемблере (язык программирования низкого уровня, близкий к машинному).

Inline (встроенная). Директива inline позволяет записывать инструкции в ма­шинном коде, не используя блок операторов. При вызове обычной процедуры компилятор создает код, в котором параметры процедуры помещаются в стек, а за­тем для вызова процедуры генерируется инструкция call. Когда вы вызываете внут­реннюю процедуру, компилятор генерирует код из директивы inline вместо call. Таким образом, inline-процедура "расширяется" при каждом обращении к ней ана­логично макрокоманде на языке ассемблера. (Макрокоманда — macro — предло­жение языка программирования, вместо которого компилятор при трансляции за­писывает несколько машинных команд.)

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

А так как до вызова процедуры она обязательно должна быть описана, то ис­пользуется опережающее объявление: процедура содержит описание только сво­его заголовка, вслед за которым ставится зарезервированное слово forward (опере­жающий), а описание текста процедуры помещается далее в тексте программы уже без повторения описания списка формальных параметров и называется опреде­ляющим объявлением.

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

Определяющее объявление процедуры может быть external или assembler. Однако оно не может быть near-; far-; inline- или другим forward-объявлением. Оп­ределяющее объявление также не может содержать директивы interrupt, near или far. Опережающие объявления не допускаются в интерфейсной части модуля. На­пример:

Как видно из примера, опережающее объявление процедуры Р1 позволило ис­пользовать обращение к ней из процедуры Р2, так как при трансляции компилятор) до вызова процедуры Р1 из опережающего объявления становятся известными ее формальные параметры и он может правильно организовать ее вызов. В самом теле процедуры Р1 уже нет необходимости описывать параметры, так как это было сде­лано при опережающем описании.

Примером программы с использованием вложенных подпрограмм-процедур может быть программа Demo_Tower, в которой реализован алгоритм древней игры "Ханойские башни". Имеются три стержня, на одном из них (например, на левом) насажены диски разных размеров, причем диски располагаются так, чтобы стер­жень с дисками напоминал башню, т. е. внизу располагаются самые большие дис­ки, а вверху маленькие. Цель игры — перенести башню с правого стержня на ле­вый, причем за один раз можно переносить только один диск и при этом можно на­саживать только диск с меньшим диаметром на диск с большим диаметром. Сред­ний стержень является вспомогательным для временного хранения дисков.

Упражнение 8. Изучите текст программы. Запустите интегрированную среду програм­мирования. Введите текст программы Demo_Tower и запишите файл на диск под соответст­вующим именем, а затем откомпилируйте его. После того как компиляция выполнится ус­пешно, задайте для просмотра в окне отладчика переменные From, Tol, Work, Hight=l. Уста­новите видимыми одновременно окна редактора с текстом программы и окно просмотра. Исполните программу в пошаговом режиме с заходом в процедуры и пронаблюдайте за ре­курсивным вызовом процедуры MoveTower.

В дальнейшем после изучения графических процедур Турбо Паскаля вы може­те представить перенос дисков со стержня на стержень графически