Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Логическое программирование. Учебно-методическое пособие к лабораторным работам
.pdf
31
Часть 2. Разработка рекурсивных типов данных
1. Разработать тип «строка» — список символов.
2. Разработать тип «многозначное число» — список цифр.
3. Разработать тип «текст» — список строк (список списков).
4. Разработать тип «бинарное дерево». Вершина дерева — его элемент
и два «листа» («ветви») — такие же «деревья».
5. Разработать тип «квадро-дерево». Вершина дерева — его элемент и
четыре «листа» («ветви») — такие же «деревья».
6. Разработать тип «стек» (LIFO).
7. Разработать тип «полином» — список коэффициентов.
8. Разработать тип «цепная дробь» — список числителей и знаменате-
лей.
9. Разработать рекурсивный тип «префиксное алгебраическое выраже-
ние».
10. Разработать тип «терм» — рекурсивную многоаргументную структуру.
Методические указания
При подготовке к выполнению и сдаче работы рекомендуется изучить
альтернативные структуры данных по гл. 3, 4, 9 [10]. Списковые типы, также
как и альтернативные домены, описаны в гл. 3, 5 [7] и в гл. 12, 14 [8]. Встроенные предикаты обработки списков рассмотрены в [12]. Различные дина-
мические структуры данных и способы их организации можно изучить по
[13].
Содержание отчета:
1) заголовок лабораторной работы;
2) цель лабораторной работы;
3) индивидуальное задание;
4) семантическую сеть базы данных;
5) описание предикатов, созданных программистом;
6) текст логической программы;
7) результаты работы программы.

32
Р а б о т а №4. РЕКУРСИВНЫЕ ЛОГИЧЕСКИЕ ПРОГРАММЫ
Цель работы — изучение возможностей создания рекурсивных логиче-
ских программ, а также осуществления арифметических и алгебраических
вычислений на ПРОЛОГе.
Общие сведения
Рекурсивные функции (одно из фундаментальных математических поня-
тий) являются основным инструментом организации повторяющихся вычис-
лений в логическом программировании. Все версии языка ПРОЛОГ допус-
кают возможность создания рекурсивных правил, имеющих следующий об-
щий вид:
<имя правила рекурсии> :-
<список предикатов>,
<предикат условия выхода>,
<список предикатов>,
<имя правила рекурсии>,
<список предикатов>.
Как видно из представленного описания, отличительной чертой рекур-
сивного правила является использование одного и того же предиката в голове и в теле правила. Такая синтаксическая конструкция при вызове рекур-
сивного правила приводит к повторным обращениям к этому правилу, что и
позволяет организовывать повторяющиеся вычисления.
Для того чтобы рекурсия не была бесконечной, чаще всего рекурсивное
правило содержит предикат условия выхода, приостанавливающий повтор-
ные обращения. (Возможное отсутствие этого предиката в ряде специаль-
ных случаев будет рассмотрено ниже, в других лабораторных работах).
Каждый из списков предикатов, входящих в правило рекурсии, может
быть, в зависимости от задачи, произвольным, в том числе и пустым.
Ни одно рекурсивное правило не должно встречаться в тексте логиче-
ской программы изолированно. Обязательно наличие хотя бы одного факта
с тем же именем, который будет определять конечную точку рекурсии. К ней
должна быть направлена рекурсия, с ней же должно быть непосредственно
связано условие выхода из рекурсии.

33
На практике для составления рекурсивной программы следует представить желаемую функцию в рекуррентном виде. В этом случае текст рекурсивного правила будет очевиден. Так функция «факториал» может быть
представлена как: 1!=1,
n!=(n-1)!*n.
Соответствующие клаузы логической программы будут иметь вид:
fact(1,1). % стартовое значение факториала (конечная точка рекурсии)
fact(N,F) :- % голова рекурсивного правила
N>1, % условие выхода из рекурсии
N1=N-1, % вычисление предыдущего шага рекурсии
fact(N1,F1), % рекурсивный вызов в теле правила
F=F1*N. % вычисление искомого значения
Типичной ошибкой при составлении рекурсивного правила является
направление рекурсии в противоположную сторону, что приводит к беско-
нечным вычислениям. В рассмотренном случае с вычислением факториала
эта ошибка случилась бы, если бы переход к следующему шагу рекурсии
произошёл не с убыванием, а с возрастанием номера (N1=N+1 вместо
N1=N-1).
Чтобы рекурсия не была бесконечной, нужно направлять её в
сторону точки останова.
В теле рекурсивного правила имя правила может встречаться более одного раза. Так при вычислении чисел Фибоначчи правило будет дважды ре-
курсивным: fib(1,1).
fib(2,1).
fib(N,F):- N>2,
N1=N-1,
fib(N1,F1), % первый рекурсивный вызов
N2=N-2,
fib(N2,F2), % второй рекурсивный вызов
F=F1+F2.
Важно заметить, что рекурсивное правило может вызывать другое рекур-
сивное правило, то есть, возможна вложенная рекурсия. В приведённом ниже

34
Примере 4 предложены программы вычисления «фигурных» чисел, а имен-
но треугольных и пирамидальных чисел. Видно, что рекурсивная клауза для
пирамидальных чисел содержит вызов клаузы треугольных чисел, то есть
осуществляется вложенное рекурсивное вычисление. (С целью использования дополнительных сервисных возможностей Турбо-ПРОЛОГа, в Примере
4 рассмотрено создание окна диалога.) В примерах рекурсий использованы
не рассматриваемые ранее синтаксические конструкции, хорошо известные
в других языках программирования, но необычные для ПРОЛОГа. Это пре-
дикаты сравнения (см. Таблицу 2), а также арифметические операции (см.
Таблицу 3).
Таблица 2. Бинарные предикаты сравнения
= «равно»
> «больше»
< «меньше»
>= «больше или равно»
<= «меньше или равно»
<> «не равно»
Таблица 3. Арифметические операции (приоритет традиционный)
а) бинарные
+ «сложение»
- «вычитание»
* «умножение»
/ «деление»
div «целая часть от деления»
mod «целый остаток от деления»
б) унарные
+ «унарный плюс»
- «унарный минус»
Предикаты сравнения для удобства используются в традиционной инфиксной форме записи, когда символ, обозначающий имя предиката, находится меж-
ду аргументами, а не в принятой в логическом программировании префиксной

35
форме, когда аргументы располагаются после символа предиката (в ПРОЛОГе методически правильней была бы запись >(3,2), означающая 3>2).
Особенно важно заметить, что классическое присвоение в ПРОЛОГе
отсутствует. Механизм преодоления этого обстоятельства будет обсуж-
даться далее в лабораторных работах. То есть, например, при выполнении
цели выражение N1=N-1 не вычисляется, а как бы подбирается подходящее
значение для N1, после чего инфиксный предикат = возвращает значение
true. Следовательно, изолированное, в отрыве от логических вычислений,
использование арифметических операций в логическом программировании
невозможно. Это же относится и к алгебраическим функциям, описанным в
Таблице 4. При этом запись алгебраических функций носит удобный именно
в логическом программировании префиксный характер.
Таблица 4. Встроенные алгебраические функции
abs(X) модуль X
sin(X) синус X
cos(X) косинус X
tan(X) тангенс X
arctan(X) арктангенс X
exp(X) экспонента X
ln(X) натуральный логарифм X
log(X) десятичный логарифм X
sqrt(X) корень квадратный из X
round(X) округление X
trunc(X) усечение (отбрасывание дробной части) X
В различных версиях ПРОЛОГа возможны несущественные изменения
этой таблицы преимущественно в сторону увеличения.
Рекурсивные программы используются не только для алгебраических
вычислений. Так, в одной из нижеследующих лабораторных работ рассмотрены рекурсивные программы обработки списков.

36
% Пример 4. Рекурсивные арифметические программы и создание окна диалога
predicates
run
treug(integer, integer)
piramid(integer, integer)
goal
makewindow(1,31,53,"ВЫЧИСЛЕНИЕ ПИРАМИДАЛЬНЫХ ЧИСЕЛ",5,12,10,56),
% создать окно №1, атрибуты окна 31 (белые буквы 15 + синий фон 16)
% и рамки 53 (лиловые символы 5 + бирюзовый фон 48 ),
% с заголовком "..." сверху от строки 5 и слева от столбца 12
% вниз на 10 строк (высота окна) и вправо на 56 столбцов (ширина окна)
run.
% вызов рекурсивного правила run для интерактивного диалога с пользователем
clauses
run :- write("\n Введите степень 'пирамидального' числа : "),
readint(Num), % считывание целого числа
piramid(Num, Y), % вызов требуемого "пирамидального" числа
write("\n значение 'пирамидального' числа: ",Y), % печать этого числа
treug(Num, X), % вызов требуемого "треугольного" числа
write("\n значение 'треугольного' числа: ", X), % печать этого числа
readchar(_), % ожидание нажатия произвольной клавиши
clearwindow, % очистка рабочего окна
run. % рекурсивный вызов правила
treug(1,1). % стартовое значение "треугольного" числа
treug(X,Y):- X>1, X1=X-1, treug(X1,Y1), Y=Y1+X.
% рекурсивное вычисление очередного "треугольного" числа через предыдущее
piramid(1,1). % стартовое значение "пирамидального" числа
piramid(X,Y):- X>1, treug(X,Z), X1=X-1, piramid(X1,Y1), Y=Y1+Z.
% рекурсивное вычисление очередного "пирамидального" числа через
% предыдущее с использованием вызова очередного "треугольного" числа

37
Вернувшись к задачам 1-й лабораторной работы, можно синтезировать
рекурсивное правило «предок», определив его как
«Предок
Такая формулировка может быть представлена клаузами:
parent(X, Y) :- mother(X,Y);
parent(X, Z), father(Z, Y).
Замечательно в этих рекурсивных клаузах то, что в явном виде условие
выхода отсутствует. Выход из рекурсии в данном случае определяется пра-
вильно заданным направлением и конечной точкой. Возможно изменение
словесной формулировки «предок»:
человека —
либо предок матери или предок отца»
parent(X, Y) :- parent(X, Z), mother(Z, Y);
это его мать или его отец
father(X, Y).
,
.
«Предок человека — это его мать или его отец
либо мать предка или отец предка»
Тогда рекурсивная программа будет выглядеть иначе:
parent(X, Y) :- mother(X, Y);
father(X, Y).
parent(X, Y) :- mother(X, Z), parent(Z, Y);
father(X, Z), parent(Z, Y).
Эти программы идентичны по результатам, но различны по вычисли-
тельным затратам. Этому аспекту рекурсивных программ посвящена следу-
ющая работа.
.
,

38
Задания для лабораторной работы №4
Часть 1. Рекурсивные арифметические вычисления
1. Вычислить таблицу чисел Фибоначчи рекуррентным способом.
2. Распечатать таблицу факториалов натуральных чисел.
3. Разработать рекурсивную программу умножения натуральных чисел,
использую только операцию сложения.
4. Разработать рекурсивную программу возведения натуральных чисел в
натуральную степень, использую только операцию умножения.
5. Рассчитать коэффициенты треугольника Паскаля (бинома Ньютона)
рекурсивным способом.
6. Написать программу вычисления n-го члена арифметической прогрес-
сии.
7. Написать программу вычисления суммы n членов арифметической
прогрессии.
8. Написать программу вычисления n-го члена геометрической прогрес-
сии.
9. Разработать таблицу простых чисел (в пределах диапазона INTEGER),
используя рекурсивный алгоритм («решето») Эратосфена для обнаружения
простых чисел среди произвольных натуральных.
10. Написать программу вычисления суммы n членов геометрической
прогрессии.
11. Осуществить вычисление наибольшего общего делителя двух нату-
ральных чисел рекуррентным способом.
12. Осуществить вычисление наименьшего общего кратного двух нату-
ральных чисел рекуррентным способом.
13. Создать таблицу «квадратных» чисел, вычислив их рекуррентным
способом.
14. Создать таблицу «кубических» чисел, вычислив их рекуррентным
способом.
15. Создать таблицу «фигурных» чисел («четырёхугольных пирамидаль-
ных»), вычислив их рекуррентным способом.

39
16. Создать таблицу «фигурных» чисел («пятиугольных пирамидаль-
ных»), вычислив их рекуррентным способом.
17. Осуществить рекурсивное вычисление числа перестановок.
18. Осуществить рекурсивное вычисление числа размещений.
19. Осуществить рекурсивное вычисление числа сочетаний.
Часть 2. Рекурсивные логические программы
При выполнении этой части лабораторной работы рекомендуется использовать данные из приложений к 1-й лабораторной работе.
1. Разработать рекурсивную программу «предок».
2. Разработать рекурсивную программу «предок по прямой отцовской ли-
нии».
3. Разработать рекурсивную программу «предок по прямой материнской
линии».
4. Разработать рекурсивную программу «потомок».
5. Разработать рекурсивную программу «потомок по прямой отцовской
линии».
6. Разработать рекурсивную программу «потомок по прямой материнской
линии».
7. Разработать рекурсивную программу «старший по званию».
8. Разработать рекурсивную программу «старший по должности».
9. Разработать рекурсивную программу «младший по званию».
10. Разработать рекурсивную программу «младший по должности».
Часть 3. Приближённые рекурсивные вычисления функций
При выполнении этой части необходимо преобразовать известные раз-
ложения в функциональные ряды известных математических функций.
1. Осуществить приближенное рекуррентное вычисление «интегрального
синуса», используя разложение в ряд Тейлора-Маклорена.
2. Вычислить приближенно «интегральный косинус», создав соответ-
ствующую рекурсивную функцию суммирования функционального ряда.

40
3. Рассчитать приближенно функцию «интегральная экспонента» с по-
мощью разложения в степенной ряд.
4. Разработать рекурсивную программу приближенного вычисления
функции «интегральный логарифм» с помощью разложения в степенной
ряд.
5. Разработать рекурсивную программу приближенного вычисления
функции arctg(x) с помощью разложения в степенной ряд.
6. Построить приближенную функцию вычисления числа e, используя
разложение в ряд Тейлора-Маклорена.
7. Построить приближенную функцию вычисления числа π, используя
разложение в степенной ряд функции arctg(x).
8. Осуществить рекурсивное суммирование тригонометрического ряда
для приближённого вычисления неаналитических функций.
9. Осуществить приближенное рекуррентное вычисление «функции ошибок», используя разложение в ряд Тейлора-Маклорена.
Методические указания
При подготовке к выполнению и сдаче лабораторной работы рекоменду-
ется ознакомиться с теоретическими определениями и обоснованиями ре-
курсивных вычислений, изложенными в [1, 3]. Рекурсивное программирова-
ние на Турбо-ПРОЛОГе можно изучить по гл. 5 [7], (а на языке Visual
Prolog — по гл. 13 [8]). Встроенные алгебраические предикаты подробно
освещены в гл. 16 [8] и в [12].
Содержание отчета:
1) заголовок лабораторной работы;
2) цель лабораторной работы;
3) индивидуальное задание на выполнение лабораторной работы;
4) математические формулы, иллюстрирующие решение задачи;
5) текст логической программы;
6) результаты работы программы;
7) выводы.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
