- •1. Рекурсивные алгоритмы
- •1.1. Основные определения
- •1.2. Накопления суммы или произведения.
- •Упражнение
- •Варианты задач
- •1.3. Быстрая сортировка
- •1.5.1.Основные определения
- •1.5.2. Пример задания некоторого языка l1 в бнф
- •1.5.3. Пример разработки программы "Синтаксический анализатор "
- •Ошибок нет Нет символа "[" Ошибка в выраженииВыходная форма:
- •2 Этап. Разбор второго правила. Раскрытие процедуры SimpExpr.
- •1.5.4. Лабораторная работа " Синтаксический анализатор "
- •Итак, метод для рассматриваемой задачи будет описан в виде синтаксических
- •На этом этапе можно продолжить проектирование в одном из двух направле-
- •2. Рекурсивный тип данных
- •Основные определения
- •2.2. Динамическое распределение памяти
- •Упражнение
- •Лабораторная работа “Генеалогическое дерево”
- •Варианты заданий
- •Литература
- •Содержание
2.2. Динамическое распределение памяти
Такое рекурсивное описание вполне корректно с точки зрения синтаксических правил языка Паскаль. Но никакая хитроумная программа не смогла бы работать с данными такой структуры. Причиной этого является противоречие между статическим описанием данных и динамической природой этих данных. Рассмотрим этот вопрос подробнее.
Язык Паскаль требует описания всех переменных, используемых в программе. Компилятор, анализируя текст Паскаль-программы, распределяет память под все описанные данные, до начала выполнения программы, сохраняя это распределение до завершения программы. Причем каждая переменная структурного типа размещается в памяти целостным куском, условный адрес которого определяется именем этой переменной. Такое распределение памяти называется статическим. В нашем примере структуру такого участка памяти условно можно изобразить в следующем виде:
op
true id
false ?
Знак "?" отражает неопределенную ситуацию для компилятора, вызванную рекурсией. Рекурсия возникает, если t = false. В этом случае объем требуемой памяти будет зависеть от конкретного обрабатываемого выражения, а это станет известно только при вводе этих данных. Другими словами, объем памяти определяется динамически, при выполнении программы.
Ниже приведены схемы распределения памяти для примеров 1-3.
1) x + y 2) (x / (y+z)) * w
op
true
x
false
y
*
/
true
x +
false
false true y
false
x
true
w
3) x * (y + z)
*
true
x
+
false
true y
false
z
На этих схемах ясно виден результат рекурсивного процесса: в области памяти фиксированного объема, обозначенной знаком "?" может разместиться простая переменная (пример 1), или вся структура (пример 2), для которой рекурсивный
процесс может повториться и мы получим следующий уровень рекурсии (пример 3). Количество таких повторений не определено заранее: то есть до конца ввода всего выражения. Такой способ распределения памяти можно сравнить с заполнением газом воздушного шара. Но так же, как шар нельзя надуть, поместив его, например, в спичечный коробок, так и динамическую структурную переменную невозможно распределить в статическую память "жесткого" объема. Другими словами, для рекурсивных структур невозможно определить фиксированный размер памяти и компилятор не может приписать компонентам такой структуры определенные адреса.
Итак, со стороны синтаксиса языка Паскаль нет никаких нарушений при описании рекурсивных типов данных. Предупреждает возможные ошибки "правило опережающего описания" языка Паскаль: прежде чем объект используется, его надо описать. В нашем примере в описании типа expression упоминается тип term, который описан дальше. Поэтому при компиляции этого фрагмента программы возникнет ошибка.
Для решения проблемы динамического распределения памяти, в частности под рекурсивный тип данных, используется ссылочный способ реализации данных.
Рекурсивная переменная не должна представлять целостную структуру в памяти, а каждый элемент структуры выделен отдельно и занимает отдельную область в любом свободном месте памяти. Связь между элементами устанавливается неявно, в виде адресных ссылок (указателей) на области памяти. Переменная ^term является ссылкой на область памяти, где размещена структура term, точно также ^expression ссылается на структуру expression.
Указателей описываются как статические переменные, но адреса им выделяются только при выполнении программы, т.е. память заполняется динамически.
TYPE
expression = record
op : operator;
opd1,opd2 : ^term
end;
term = record
case t: Boolean of
true: (id: alfa);
false: (subex: ^expression)
end;
Пример 2. Генеалогическое дерево состоит из имени человека и двух генеалогических деревьев его родителей. Такое рекурсивное определение неизбежно приводит к бесконечной структуре. Реальные генеалогические структуры конечны, потому что на каком-то уровне сведения о предках отсутствуют. Если мы используем для описания рекурсивную структуру, то она будет иметь следующий вид:
true
Ted
true Fred
true Adam
false
false
false
true
Mary
false
true
Eva
false
false
type ped = record
father, mother: ped)
end;
Для конкретного примера структура
г
б
Ted
Adam Eva
У
структуру своего генеалогического дерева.