- •Средства разработки программ на Паскале
- •Структура Паскаль-программы
- •Комментарии
- •Директивы компилятора
- •Идентификаторы
- •Переменные и типы данных
- •Константы
- •Неименованные константы
- •Нетипизированные константы
- •Типизированные константы
- •Простейшие операторы
- •Метки и безусловный переход
- •Ввод и вывод: консоль
- •Ввод с консоли
- •Вывод на консоль
- •Форматный вывод
- •Пример простейшей программы на языке Pascal
- •Типы данных
- •Порядковые типы данных
- •Стандартные подпрограммы, обрабатывающие порядковые типы данных
- •Типы данных, относящиеся к порядковым
- •Вещественные типы данных
- •Конструируемые типы данных
- •Операции и выражения
- •Совместимость типов данных
- •Приведение типов данных
- •Ветвления, массивы, циклы
- •Массивы
- •Операторы циклов
- •Сортировки массивов
- •Быстрая сортировка
- •Символы и строки
- •Неименованные константы
- •Нетипизированные константы
- •Типизированные константы
- •Операции
- •Стандартные функции
- •Стандартные функции и процедуры обработки строк
- •Операции со строками Сравнения
- •Обращение к компонентам строки
- •Конкатенация
- •Множества
- •Описание множеств
- •Множество-константа Неименованная константа
- •Нетипизированная константа
- •Типизированная константа
- •Операции с множествами
- •Представление множеств массивами
- •Представление множеств линейными массивами
- •Представление множеств битовыми массивами
- •Примеры использования символов, строк и множеств
- •Что такое файл
- •Когда нужно использовать файлы
- •Разновидности файлов
- •Описание файлов
- •Текстовые файлы Назначение файла
- •Открытие файла
- •Закрытие файла
- •Считывание из файла
- •Запись в файл
- •Пробельные символы
- •Пример использования файлов
- •Решение
- •Реализация
- •Изменение реакции на ошибку
- •Описание записей
- •Задание записей константой
- •Доступ к полям
- •Оперирование несколькими полями
- •Вложенные операторы with
- •Запись с вариантной частью
- •Описание записи с вариантной частью
- •Механизм использования записи с вариантной частью
- •Бинарные файлы
- •Типизированные файлы
- •Описание типизированных файлов
- •Назначение типизированного файла
- •Открытие и закрытие типизированного файла
- •Считывание из типизированного файла
- •Поиск в типизированном файле
- •Запись в типизированный файл
- •Поиск в нетипизированном файле
- •Запись и чтение
- •Подпрограммы обработки директорий
- •Применимость подпрограмм обработки файлов
- •Процедуры и функции Подпрограммы
- •Список параметров
- •Возвращаемые значения
- •Вызов подпрограмм
- •Способы подстановки аргументов
- •Параметр-значение Описание
- •Механизм передачи значения
- •Параметр-переменная Описание
- •Механизм передачи значения
- •Параметр-константа Описание
- •Механизм передачи значения
- •Области действия имен Разграничение контекстов
- •Побочный эффект
- •Совпадение имен
- •Нетипизированные параметры
- •Явное преобразование типа
- •Совмещение в памяти
- •Открытые параметры
- •Открытые массивы
- •Рекурсивные подпрограммы Динамические структуры данных
- •Операции
- •Очередь
- •Операции
- •Рекурсия
- •Рекурсивные подпрограммы
- •Пример рекурсивного алгоритма
- •Алгоритм решения
- •Стековая организация рекурсии
- •Ограничение глубины рекурсии
- •Замена рекурсивных алгоритмов итеративными
- •Пример сравнения рекурсивного и нерекурсивного алгоритма
- •Рекурсивный алгоритм
- •Реализация рекурсивного алгоритма
- •Полный перебор с отсечением
- •Нерекурсивный алгоритм
- •Реализация нерекурсивного алгоритма
- •Иллюстрация
- •Эффективность
- •Быстрая сортировка2
- •Алгоритм Быстр
- •Реализация алгоритма Быстр
- •Эффективность алгоритма Быстр
- •Адреса и указатели. Списочные структуры данных Статически выделяемая память
- •Разыменование
- •Присваивания
- •Сравнения
- •Динамически распределяемая память
- •Динамическое выделение памяти Типизированные указатели
- •Нетипизированные указатели
- •Динамическое освобождение памяти Типизированные указатели
- •Нетипизированные указатели
- •Списочные структуры
- •Структура списков
- •Описание списков
- •Оперирование элементами списка Хранение списка
- •Обращение к элементам списка
- •Создание списков
- •Просмотр элементов списка
- •Удаление элементов списка
- •Перестройка списков
- •Примеры перестройки линейных списков
- •Реализация
- •Создание дружественного интерфейса
- •Заставка
- •Ввод информации
- •Приглашения
- •Вывод информации
- •Технология программирования и отладка Советы по технологии написания быстро отлаживаемых программ
- •Имена, имена, имена...
- •Кусочки, куски и кусищи...
- •Спасение утопающих - дело рук самих утопающих
- •Отладка и тестирование
- •Поиск и исправление ошибок
- •Правила составления тестов
- •Оптимизация программ
- •Учебники к курсу
Ограничение глубины рекурсии
Теоретически, рекурсия может быть бесконечной. Однако такой вариант вряд ли кого-нибудь устроит: рекурсивный алгоритм, как и любой нерекурсивный его собрат, обязан выдавать результат своей работы за некое обозримое время. Кроме того, память у компьютера не резиновая, в ней может поместиться лишь конечное число контекстов одновременно открытых экземпляров рекурсивной подпрограммы.
Следовательно, каждая рекурсивная подпрограмма должна содержать в себе признак окончания - своеобразный "забор", определяющий максимальную глубину вложенности для этой рекурсии. Признак конца рекурсии может быть как явным (например, в случае реализации факториала), так и неявным (в частности, описанная выше процедура razlozh рано или поздно обязательно закончится, поскольку на каждом шаге происходит уменьшение разлагаемого натурального числа).
Замена рекурсивных алгоритмов итеративными
Поскольку новый контекст создается каждый раз, когда очередной экземпляр рекурсивной подпрограммы (сам еще оставаясь незавершенным) заново вызывает себя же, то память компьютера расходуется очень быстро. Поэтому рекурсию при всей ее наглядности нельзя отнести к экономичным способам программирования. Существует даже специальная наука - динамическое программирование, изучающая способы замены рекурсивных алгоритмов адекватными итеративными алгоритмами. Мы не станем сейчас углубляться в изложение принципов динамического программирования, а ограничимся лишь примерами превращения рекурсивных программ в нерекурсивные.
Если исполнение подпрограммы приводит только к одному вызову этой же самой подпрограммы, то такая рекурсия называется линейной. Линейную рекурсию довольно легко заменить итеративным алгоритмом. Например, можно реализовать функцию факториала, определенную в начале пункта "Рекурсия", двояко.
Рекурсивная реализация |
Итеративная реализация |
function fact(k:byte): longint; var x: longint; begin if k = 0 then fact:= 1 else begin x:= fact(k-1)*k; fact:=x; end; end; |
fact:= 1 for i:= 2 to k do fact:= fact * i; |
Если же каждый экземпляр подпрограммы может вызвать себя несколько раз, то рекурсия называется нелинейной или "ветвящейся". Для того чтобы превратить такую рекурсию в итеративный алгоритм, придется приложить много усилий и, возможно, даже внести некоторые изменения в обрабатываемую структуру данных. Примером "ветвящейся" рекурсии может служить рассмотренная выше процедура разложения числа на сомножители.
Пример сравнения рекурсивного и нерекурсивного алгоритма
Задача. Двое друзей решили пойти в поход, собрали и взвесили все необходимые вещи. Как им разделить набор предметов на две части наиболее честным образом?
(Имеется набор натуральных чисел, быть может, с повторениями. Необходимо разделить его на два поднабора так, чтобы разность сумм весов была минимальной.)
Рекурсивный алгоритм
Нужно перебрать все возможные подмножества заданного набора весов. Для решения этой задачи существует несколько классических алгоритмов, мы воспользуемся простейшим, который носит название "полный перебор". В полном соответствии со своим названием, этот алгоритм перебирает все возможные варианты наборов.