- •Введение Цели и задачи курса «Технология программирования»
- •Классификация программного обеспечения
- •Тема 1.Понятие программного изделия
- •1.1.Основные требования к программному изделию как к продукции производственно-технического назначения
- •1.2.Жизненный цикл программных изделий
- •1.3.Методическая, технологическая, инструментальная и организационная поддержка процесса проектирования программных изделий
- •1.4.Этапы проектирования сложных программных изделий
- •Тема 2.Внешнее проектирование программных изделий
- •2.1.Разработка требований и внешнее проектирование программных изделий
- •2.2.Обеспечение целей создания программных изделий Цели проекта с точки зрения пользователя
- •Цели проекта с точки зрения разработчика
- •2.3.Разработка внешних спецификаций проекта
- •Тема 3.Внутреннее проектирование программных изделий
- •3.1.Понятие модуля. Характеристики качества модулей
- •3.2.Методы проектирования программ
- •Иерархическая декомпозиция и абстрактные машины (метод нисходящей декомпозиции)
- •Программирование на основе принципа пошагового совершенствования
- •Структурное программирование по нисходящему принципу
- •Программирование с использованием пошаговой реорганизации
- •Способы описания программ. Язык проектирования программ
- •Тема 4.Структурированные программы
- •4.1.Блок-схемы и управляющие структуры
- •4.2.Теорема о структурировании
- •4.3.Рекурсивные структурированные программы
- •Тема 5.Методы Доказательства правильности программ
- •5.1.Математический аппарат доказательства Принцип простой индукции
- •Принцип модифицированной простой индукции
- •Принцип обобщенной индукции
- •5.2.Методы доказательства правильности программ Метод индуктивных утверждений
- •Пример доказательства правильности программы методом индуктивных утверждений
- •Тема 6.Рекурсивные программы. Методы доказательства их правильности
- •6.1.Понятие рекурсивных программ. Способы их описания Язык описания рекурсивных программ
- •Примеры рекурсивных программ
- •6.2.Рекурсивные программы работы со списками Списки и операции над ними
- •Примеры программ работы со списками
- •6.3.Примеры доказательства правильности рекурсивных программ
- •Тема 7.Отладка программ
- •7.1.Типичные ошибки в программных комплексах
- •7.2.Задачи отладки программ
- •7.3.Методы разработки тестов Метод эквивалентных разбиений
- •Методы тестирования программ путем покрытия логики
- •Метод функциональных диаграмм
- •Предположения об ошибках
- •7.4.Задачи комплексной отладки программ
- •Библиографический список
Примеры рекурсивных программ
1. Рассмотрим программу, применимую целого:
F (x) = if х=0 then 1
else х·F(х-1)
Для иллюстрации работы данной программы выполним ее, например, для х=4:
2. Функция Аккермана. Эта функция применима целых.
А(х,у) = if х=0 then у+1
else if y=0 then A(x-1,1)
else A(х-1, A(x, y-1))
Проследим, каким образом выполняется эта программа для х=1, у=2.
А(1,2)=А(0, А(1,1))=А(0, А(0, А(1,0)))=
=А(0, А(0, А(0,1)))=А(0, А(0,2))= А(0,3) =4
Из этого примера видно, что при вычислении рекурсивных функций могут возникать неоднозначности, связанные с последовательностью вычисления аргументов. Например, при обращении А(0, А(1,1)) мы предполагали, что А(1,1) должна быть вычислена прежде, чем будут продолжены вычисления, относящиеся к внешнему обращению к функции А. Если отдать предпочтение вычислениям, связанным с внешним обращением, то вычисления будут производиться в следующем порядке:
А(1, 2) = А(0, А(1,1)) = А(1,1) + 1 =
= А(0, А(1,0)) + 1= (А(1,0) +1) + 1 =
= А(0,1) + 2 = 2 + 2 = 4
Можно доказать, что если к одним и тем же аргументам некоторой рекурсивной функции применять две разные последовательности ее вычисления, то результат будет одним и тем же при условии конечности этих последовательностей. Однако возможны случаи, когда последовательность не будет конечной. Рассмотрим следующий пример для демонстрации этого факта.
Рассмотрим следующую функцию, применимую целых:
F (x,у) = if х=0 then 0
else F(0, F(х,у))
Проследим за вычислением F, используя два различных правила вычисления:
при предпочтении внешнему обращению
F (1,1) = F(0, F(1,1))=0;
при предпочтении внутреннему обращению.
F (1,1) = F(0, F(1,1))= F(0, F(0,F(1,1)))=…
Таким образом, в первом случае последовательность конечна и функция А=0, во втором случае вычисления не заканчиваются.
Для того чтобы устранить неопределенность, необходимо задать правила вычисления. Большинство языков программирования при обращении к функции (процедуре) предварительно вычисляют аргументы, отдавая тем самым предпочтение внутреннему вызову.
6.2.Рекурсивные программы работы со списками Списки и операции над ними
Большинство рекурсивных алгоритмов связано с работой со списками в силу рекурсивного определения самих структур данных - списков. Для описания этих алгоритмов введем следующую нотацию.
Список – это набор объектов, отделенных друг от друга пробелами и заключенных в квадратные скобки []. Объектами, входящими в такие списки, являются атомы (неделимые элементы) или другие списки.
Атом – это строка буквенно-цифровых символов, не содержащая пробелы.
Для того чтобы отличать атомы от переменных, будем считать, что переменные записываются строчными буквами, а атомы – прописными. Например, [А В С] – список из трех элементов, каждый из которых является атомом. [А [В А [С]]D] – список, в котором на верхнем уровне три элемента: второй элемент сам является списком из трех элементов, в котором третий элемент – это список из одного элемента С.
Для пустого списка, т.е. списка, не содержащего одного элемента, выделяется специальное имя nil.
Наиболее распространенными операциями над списками и атомами являются следующие.
Проверка на равенство:
[А В] = [А В] выдает true ,
[А В] = [В А] и во всех других случаях - false.
Проверка аtom(х) - применима к любым объектам: атомам или спискам. Выдает true, если х является атомом или пустым списком.
Саr(l) применима к любому непустому списку. В качестве результата выдается первый на верхнем уровне элемент этого списка:
Саr([А В С])=А;
Саr([[А В]С D])=[А В];
Саr(nil) и Саr (А) – не определены.
Cdr(l) – применима к любому непустому списку l; результатом является список, полученный из списка l путем исключения первого элемента: Cdr([А В С]) = [В С].
Соns(х,l) - объединяет элемент х со списком l:
Соns(А, [D E]) = [А D Е];
Соns([А В], [D E]) = [[А В] D E].