Скачиваний:
42
Добавлен:
22.05.2015
Размер:
145.92 Кб
Скачать

Фигуры на плоскости

Пример 10. Найдём все возможные отрезки на нашем множестве из девяти точек. Для этого нам надо вызвать факт point(X1,Y1) для определения начала отрезка и факт point(X2,Y2) для определения конца отрезка:

implement main

    open core, console

constants className = "com/visual-prolog/main". classVersion = "$JustDate: $$Revision: $".

class facts

point : (integer X, integer Y).

class predicates

отрезок : (integer StartX, integer StartY,

integer EndX, integer EndY) nondeterm anyflow.

clauses classInfo(className, classVersion).

point(1,1). point(1,2). point(1,4). point(2,1). point(2,2). point(2,4). point(3,2). point(4,1). point(4,3).

отрезок(X1,Y1,X2,Y2) :- point(X1,Y1), point(X2,Y2).

run():-    init(),    отрезок(X1,Y1,X2,Y2),    writef("Найден отрезок (%,%)-(%,%)\n", X1,Y1,X2,Y2),    fail();    write("Всё!!!"),    _=readchar().

end implement main

goal

   mainExe::run(main::run).

После запуска программы таких отрезков будет 81. Почему так много? Для каждой точки, служащей началом отрезка (а таких - 9) Пролог находит все 9 вариантов конца отрезка. Итого получается 81. Причём сначала для первого варианта начала отрезка он находит 9 вариантов окончания отрезка, включая и само начало отрезка, потом для второго варианта начала отрезка он находит 9 вариантов окончания отрезка, и т.д. Получается цикл в цикле, с помощью которых Пролог находит декартово произведение множества само на себя – все возможные пары точек. Во внешнем цикле, задаваемом предикатом point(X1,Y1) ищется начало отрезка, а во внутреннем цикле, задаваемом предикатом point(X2,Y2) – конец отрезка.

Вот первые 10 решений:

Найден отрезок (1,1)-(1,1)

Найден отрезок (1,1)-(1,2)

Найден отрезок (1,1)-(1,4)

Найден отрезок (1,1)-(2,1)

Найден отрезок (1,1)-(2,2)

Найден отрезок (1,1)-(2,4)

Найден отрезок (1,1)-(3,2)

Найден отрезок (1,1)-(4,1)

Найден отрезок (1,1)-(4,3)

Найден отрезок (1,2)-(1,1)

Из этих решений видно, что для точки (1,1), служащей началом отрезка, Пролог нашёл все возможные варианты конца отрезка. После чего в качестве начала отрезка избрал точку (1,2) а повторил поиск всех вариантов конца отрезка.

Пояснение для грамотных:

Рассмотрим более подробно, почему именно так всё и происходит. Пролог при нахождении начала отрезка – очередного факта point(X1,Y1), запоминает в стеке адрес возврата и переходит к поиску конца отрезка – факта point(X2,Y2), находит его, запоминает ещё один адрес возврата и выводит соответствующее сообщение о нахождении отрезка. Таким образом, на данный момент времени в вершине стека хранится адрес возврата к поиску нового конца отрезка, а под ним – адрес возврата для поиска нового начала отрезка. При достижении предиката fail() происходит откат на адрес, хранящийся в вершине стека, то есть к новому факту point(X2,Y2), и выводится сообщении о нахождении нового отрезка. Таким образом, для точки (X1,Y1) во время откатов будут найдены все варианты точки (X2,Y2). Причём при нахождении последнего девятого варианта конца отрезка в стек не будет записан адрес возврата, так как возвращаться больше не к чему. И при очередном откате назад Пролог перейдёт к поиску нового варианта начала отрезка, благо – адрес этого нового варианта хранится в вершине стека. Для нового варианта начала отрезка поиск конца отрезка повторится с начала.

При запуске можно увидеть, что Пролог выводит не только вырожденные в точку отрезки [A,A], у которых координаты начала и конца совпадают, но и каждый отрезок [A,B] дублирует выводом отрезка [B,A], в котором начало и конец поменяны местами.

Для подавления вывода вырожденных в точку отрезков достаточно проверить то, что координаты начала и конца отрезка не равны друг другу.

Для подавления дублирования одинаковых отрезков [A,B] и [B,A] надо использовать дополнительные инструменты Пролога, которые не являются частью машины логического вывода. Эти инструменты мы будем изучать на последующих занятиях.

Пример 11. Для нахождения всех невырожденных в точку отрезков модифицируем программу следующим образом:

class predicates

отрезок : (integer StartX, integer StartY,

integer EndX, integer EndY) nondeterm anyflow.

равны : (X,Y,X,Y) determ (i,i,i,i).

clauses

отрезок(X1,Y1,X2,Y2) :- point(X1,Y1), point(X2,Y2),

not(равны(X1,Y1,X2,Y2)).

равны(X,Y,X,Y).

Мы добавили проверку того, что концы отрезка не могут являться одной и той же точкой, с помощью нового предиката равны/4. Как и следовало ожидать, число выводимых отрезков уменьшилось до 72.

Пример 12 Усовершенствуем предикат отрезок/4 так, чтобы он возвращал кроме всего прочего и длину отрезка:

class predicates

отрезок : (integer StartX, integer StartY, integer EndX, integer EndY, real Длина) nondeterm anyflow. равны : (X,Y,X,Y) determ (i,i,i,i).

clauses point(1,1). point(1,2). point(1,4). point(2,1). point(2,2). point(2,4). point(3,2). point(4,1). point(4,3).

отрезок(X1,Y1,X2,Y2,D) :- point(X1,Y1), point(X2,Y2),

   not(равны(X1,Y1,X2,Y2)),    D=math::sqrt(math::sqr(math::abs(X1-X2)) +                  math::sqr(math::abs(Y1-Y2))).

равны(X,Y,X,Y).

run():-

   init(),

   отрезок(X1,Y1,X2,Y2,D), 2<=D, D<=3,

   writef("Найден отрезок: (%,%)-(%,%)  длина: %.3  \n", X1,Y1,X2,Y2,D),

   fail();

   write("Всё!!!"),

   _=readchar().

В этом примере осуществляется вывод только тех отрезков, которые имеют длину, удовлетворяющую условию 2<=D<=3 .Обратите внимание, каким образом форматируется вывод длины до трёх знаков после запятой.

Задание 8. Модифицировать цель в примере 8 так, чтобы на экран выводились все горизонтальные отрезки, невырожденные в точку.

Задание 9. Найти все невырожденные треугольники с периметром большим 9.

Задание 10. Найти все невырожденные равнобедренные треугольники.

Задание 11. Найти все невырожденные равносторонние треугольники.

Задание 12. Найти все квадраты, стороны которых параллельны координатным осям.

Соседние файлы в папке Лабораторные работы