На этом шаге мы рассмотрим факты и правила в качестве процедур.
Можно рассматривать правила Пролога как определения процедур. Например, правило:
likes(bill,Something):- likes(cindy,Something)
означает:
"Для того чтобы доказать, что Билл любит что-то, необходимо доказать, что Синди любит это".
Таким образом, видим, что предикаты типа:
say_hello:- write("Hello"),nl.
greet:- write("Hello,Earthlings!"), nl.
соответствуют подпрограммам и функциям в других языках программировании.
Вы можете рассматривать даже факты Пролога, как процедуры; например, факт
likes(bill,pasta)
означает:
"Для того чтобы доказать, что Билл любит pasta, не нужно ничего делать, и если аргументы Who и What в вашем запросе likes(Who,What) - свободные переменные, то вы можете присвоить им значения bill и pasta, соответственно". Далее мы покажем, как известные процедуры программирования (условное ветвление, булевы выражения, безусловные переходы и возвращение результата вычисления) могут быть реализованы в Прологе.
Одно из основных различий между правилами в Прологе и процедурами в других языках программирования заключается в том, что Пролог позволяет задавать множество альтернативных определений одной и той же процедуры. Это видно по "семейной" программе pro15_2.pro на шаге 15. Человек может быть предком, будучи отцом или матерью, поэтому определение предка состоит из двух правил.
Вы можете использовать множество определений так же, как вы применяете предложение case в Pascal, задавая множество альтернативных определений для каждого значения аргумента (или множества значений аргумента). Пролог же будет перебирать одно правило за другим, пока не найдет то, которое подходит, и затем выполнит действие, заданное правилом (как в следующей программе pro33_1.pro).
predicates action(integer) clauses action (1):- nl, write("You typed 1."),nl. action(2):- nl, write("You typed two."),nl. action(3):- nl, write("Three was what you typed."),nl. action(N):- nl, N<>1,N<>2,N<>3, write("I don't know that number!"),nl. goal write("Type a number from 1 to 3: "), readint(Choiсe), action(Choiсe).
Результат работы программы можно посмотреть на рис.1
Рис.1. Результат работы программы pro33_1.pro
Если пользователь нажмет клавиши <1>, <2> или <3>, action будет вызвана с coответствующим значением аргумента и будет вызвано одно из первых трех правил этого примера.
Посмотрите более внимательно на четвертое правило для action. Оно будет сопоставлено для любого аргумента, переданного правилу. Если вы хотите быть уверенными, что оно не напечатает I don't know that number (Я не знаю такого числа) когда число попадает в правильный диапазон, - это задача для подцелей
X<>1, X<>2, X<>3
Программа pro33_1.pro не совсем корректна из-за того, что после выбора и выполнения нужного правила Пролог продолжает поиск альтернатив.
Вы могли бы сэкономить ресурсы и время, если бы указали, где нужно прекратить поиск альтернатив, используя отсечение. Это означает:
"Если вы дошли до этого места, то не нужно производить откаты внутри этого правила и не нужно проверять остальные альтернативы этого правила".
Возвращение все еще возможно, но только на более высоком уровне. Если текущее правило вызывается другими правилами, и высшие правила имеют альтернативы, они могут быть испробованы. Но отсечение отбрасывает альтернативы внутри правила и альтернативы данного правила (предиката).
Используя отсечение (cut), программа pro33_1.pro может быть переписана следующим образом:
predicates action(integer) clauses action(1):-!, nl, write("You typed 1."). action(2):-!, nl, write("You typed two."). action(3) :-!, nl, write("Three was what you typed."). action(_):- write("I don't know that number!"). goal write("Type a number from 1 to 3: ") , readint(Num), action(Num), nl.
Отсечение не имеет никакого эффекта, пока оно не будет выполнено реально. В приведенном выше примере, для того чтобы выполнить отсечение, Пролог должен войти в правило, содержащее отсечение и достичь точки, где расположено отсечение.
Отсечение может бьпъ представлено другими примерами:
action(X):- Х>3, !, write("Too high.").
Заметьте, что порядок правил здесь имеет значение. В программе pro33_1.pro вы могли написать правила в любом порядке; только одно из них сопоставлялось с конкретным числом. Но в примере pro33_2.pro вы должны быть уверены, что компьютер не сделает попытки выполнить правило, печатающее "Я не знаю такого числа", раньше, чем будут испробованы (и не выполнят своих отсечений) все предыдущие правила.
Отсечения в программе pro33_2.pro иногда называют красными отсечениями, т. к. они меняют логику программы. Если вы сохраните проверки X<>1, Х<>2 и Х<>3, изменив программу только вставкой отсечений в каждом предложении, то вы сделаете зеленые отсечения. Они экономят время и, тем не менее, оставляют программу такой же правильной, как и без отсечений. Выигрыш при этом не так велик, но риск внести ошибку в программу уменьшается.
Отсечение - это мощный, но и опасный оператор Пролога. В этом отношении oн соответствует предложению GoTo, и остальных языках программирования он многое позволяет, но делает нашу программу более трудной для понимания.
Как мы уже видели, правила и факты Пролога могут возвращать информацию в цель, которая их вызывает. Это делается путем связывания переменных, которые были ранее не связанными.
Факт:
likes(bill,cindy).
Возвращает информацию в цель
likes(bill,Who).
путем присваивания переменной Who значения cindy.
Правило может возвращать тем же способом и результат вычислений. В программе pro33_3.pro приведен пример.
predicates classify(integer,symbol) clauses classify(0,zero). classify(X,negative):- X < 0. classify(X,positive):- X > 0.
Первый аргумент classify должен всегда получать константу или связанную переменную. Второй аргумент может быть связанной или свободной переменной, oн сопоставляется с символами zero, negative, pozitive в зависимости от значения первого аргумента.
Здесь приведены несколько примеров правил, которые могут возвращать значения.
1. Вы можете узнать, положительно ли число:classify(45,positive). да
2. Если cопоставление неуспешно, вы получаете ответ no (нет):
classilу(45,negative). нет
Что происходит при этом:
3. Для получения правильного ответа, а не yes или nо, вы должны вызвать classify со свободным вторым аргументом.
classify(45,What). What=positive 1 решение
На следующем шаге мы рассмотрим простые объекты данных.