- •64 Жегуло а.И. Компьютерные науки 2-й семестр 2011-2012 гг. Компьютерные науки Лекции для студентов 1 курса мехмата, 2011-2012 уч. Г.
- •2 Семестр
- •16.Модули
- •16.1.Модуль и модульное программирование
- •16.2.Структура модуля
- •Interface //Раздел интерфейса
- •Implementation //Раздел реализации
- •Initialization //Раздел инициализации
- •Пример модуля и его использования
- •Interface //Раздел интерфейса
- •Implementation //Раздел реализации
- •Заголовок модуля
- •Раздел интерфейса interface
- •Раздел реализации implementation
- •Раздел инициализации initialization
- •16.4.Использование объявленных в модуле объектов
- •17.Файлы
- •17.1.Файлы. Классификация файлов
- •17.2.Организация работы с файлами
- •17.3.Подпрограммы для работы с файлами любых типов Связывание файловой переменной с файлом
- •Запись в файл
- •17.5.Обработка ошибок ввода-вывода
- •17.6.Текстовые файлы
- •17.6.1.Структура текстового файла
- •17.6.2.Особенности открытия текстовых файлов
- •17.6.3.Особенности чтения и записи для текстовых файлов
- •17.6.7.Поэлементная обработка текстовых файлов, состоящих из строковых значений и чисел
- •17.7.Типизированные файлы
- •17.7.1.Описание и структура типизированного файла
- •17.7.5.Сравнение текстовых и типизированных файлов
- •17.7.6.Пример работы с типизированными файлами из записей
- •18.Процедурные типы
- •18.1.Назначение процедурных типов
- •18.2.Описание процедурных типов и процедурных переменных
- •18.3.Присваивание процедурным переменным. Вызов подпрограмм через процедурные переменные
- •18.4.Процедурные переменные в качестве параметров
- •19.Рекурсия
- •19.1.Что такое рекурсия
- •19.2.Рекурсивные подпрограммы
- •19.3.Прямая и косвенная рекурсия
- •19.4.Предварительное (опережающее) описание подпрограммы
- •19.5.Опасности рекурсии
- •19.5.1.Бесконечная рекурсия
- •19.5.2.Глубокая рекурсия
- •19.5.3.Итерация и рекурсия, необоснованное применение рекурсии
- •19.6.Когда использовать рекурсию
- •19.7.Формы рекурсивных подпрограмм
- •19.8.Примеры рекурсивных программ
- •19.8.1.Вывод цифр целого числа в прямом порядке
- •19.8.2.Поиск максимального элемента массива
- •19.9.Задача о Ханойских башнях
- •20.Указатели
- •20.1.Указательные типы
- •Описание типизированного указателя
- •20.2.Операции с указателями
- •20.3.Примеры присваивания для указателей
- •20.4.Статические и динамические переменные
- •Создание новой динамической переменной базового типа и установка на нее указателя
- •Уничтожение динамической переменной, на которую ссылается указатель
- •Проблема потерянных ссылок
- •21.Динамические структуры данных
- •21.1.Данные статической структуры и данные динамической структуры
- •21.2.Односвязные линейные списки
- •21.2.1.Структура односвязного линейного списка
- •Вставка элемента после заданного элемента
- •21.3.Стеки
- •21.3.1.Реализация стека через односвязный линейный список
- •21.3.2.Применение стеков
- •21.3.3.Реализация стека на основе массива
- •21.4.Деревья
- •21.4.1.Основные определения
- •Алгоритм построения идеально сбалансированного дерева
- •21.4.3.Способы обхода дерева
- •Деревья поиска
- •Построение дерева поиска
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.Опасности рекурсии
При применении рекурсивных алгоритмов следует избегать трех основных опасностей:
Бесконечной рекурсии.
Необоснованного применения рекурсии. Обычно это происходит, если алгоритм типа рекурсивного вычисления чисел Фибоначчи многократно вычисляет одни и те же промежуточные значения.
Глубокой рекурсии. Если алгоритм достигает слишком большой глубины рекурсии, он может привести к переполнению стека. Минимизировать использование стека можно за счет уменьшения числа определяемых в подпрограмме переменных, использования глобальных переменных. Если стек все равно переполняется, следует переписать алгоритм в нерекурсивном виде.
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, и условие вызова перестанет выполняться.