Функция определяется тремя взаимоисключающими правилами.
Воспользуемся декларативным принципом Пролога —
КАЖДОЙ АЛЬТЕРНАТИВЕ — СВОЕ УТВЕРЖДЕНИЕ:
f(X,0):- X<3.
f(X,1):- X>=3, X<=5.
f(X,2):- X>5.
При запросе
goal
f(1,Y), Y>2.
первое правило даст отказ, затем будут проверены второе и третье, затем система ответит «нет».
Очевидно, что если выполнилась одна из трех альтернатив, нет смысла проверять остальные. Укажем системе, что возврат не нужен:
f(X,0):- X<3,!.
f(X,1):- X<=5,!.
f(X,2):- X>5.
Если сейчас мы ПОМЕНЯЕМ МЕСТАМИ УТВЕРЖДЕНИЯ, ТО МОЖЕТ ИЗМЕНИТЬСЯ ТОЛЬКО ЭФФЕКТИВНОСТЬ ПРОГРАММЫ, НО НЕ ЕЕ ПРАВИЛЬНОСТЬ.
Отсечения, которые меняют процедурное поведение программы, но не ее декларативный смысл, называются ЗЕЛЕНЫМИ.
Заметим, что третья альтернатива не нуждается в проверке, так как получается автоматически при отказе первых двух:
f(X,0):- X<3,!.
f(X,1):- X<=5,!.
f(X,2).
Теперь ПРИ ПЕРЕСТАНОВКЕ УТВЕРЖДЕНИЙ ПРОГРАММА БУДЕТ ДАВАТЬ НЕВЕРНЫЕ РЕЗУЛЬТАТЫ.
Отсечения, которые меняют декларативный смысл программы, называются КРАСНЫМИ.
Далеко не всегда возникает необходимость в использовании «красных» отсечений. Запрограммируем функцию, выбирающую максимум из двух чисел (пример 4.3)
Пример 4.3.
max(real,real,real).
max(X,Y,Y):-
Y>=X,!. % «зеленое отсечение»
max(X,Y,X):-
Y<X.
Перепишем ее с использованием «красного» отсечения:
max(X,Y,Y):-
Y>=X,!. % ”красное отсечение”
max(X,Y,X).
При запросах типа max(3,7,M) процедура будет вести себя правильно, но на запрос max(3,7,3) (максимум из 3 и 7 равен 3 ?) система ответит «да»...
Исправим процедуру:
max(X,Y,M):-
Y>=X,!,M=Y.
max(X,Y,X):-
Y<X.
Но лучшим решением все же был первый вариант с использованием «зеленого» отсечения.
Рассмотрим «геометрический» пример на использование альтернатив (пример 4.4)
Пример 4.4.
На плоскости задан круг радиуса R, вписанный в квадрат с центром в начале координат. Сколько точек, брошенных на плоскость, попадут:
— вне квадрата;
— внутрь круга;
— вне круга, но внутри квадрата.
Составим вначале программу, которая будет показывать местонахождение одной точки.
/* Программа 4.2 «Точка». Назначение: */
/* демонстрация использования отрицания */
/* при программировании альтернатив. */
predicates
show_point % показать точку
%вычисление места точки
pos_point(real,real,real)
out_sqr(real,real,real) % вне квадрата
in_circ(real,real,real) % внутри круга
goal
show_point.
clauses
show_point:-
write(" R? "), readreal(R),
write(" X? "), readreal(X),
write(" Y? "), readreal(Y),
pos_point(X,Y,R).
pos_point(X,Y,R):-
out_sqr(X,Y,R),
write(" вне квадрата"), nl.
pos_point(X,Y,R):-
in_circ(X,Y,R),
write("внутри круга"), nl.
pos_point(X,Y,R):-
not(out_sqr(X,Y,R)),
not(in_circ(X,Y,R)),
write("вне круга,внутри квадрата"),nl.
out_sqr(X,Y,R):- % точка вне квадрата
abs(X)>R;
abs(Y)>R.
in_circ(X,Y,R):- % точка внутри круга
X*X+Y*Y<R*R.
/* Конец программы */
Упражнение 4.2.
Модифицировать программу 4.2, использовав отсечение ! вместо отрицания not при программировании альтернатив.
Теперь будем запрашивать точки в цикле.
Испробуем наш испытанный метод — возврат после неудачи:
show_point:-
write(" R? "), readreal(R),
write(" X? "), readreal(X),
write(" Y? "), readreal(Y),
pos_point(X,Y,R),
fail.
Увы, предикат read согласуется ровно один раз и никогда не передоказывается при возвратах. Поэтому после первой же прочитанной точки мы выйдем из программы.