Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
пролог.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
498.52 Кб
Скачать

Упражнения

1. Определите, будет ли каждая из следующих пар структур сопоставимой. Если пара сопоставима, найдите значения, которыми будут замещаться переменные в структурах. а) data(day(sunday), number(21), month(M), year(1995))  и  data(Day, Number, Month, Year) б) book(title(“Ферма животных”), name(“Дж. Оруэлл”))  и  book(title(T), Name) в) animal(cat(“лапы”), dog(“клыки”)) и  animal(Animal, Animal)

3.8. Средства управления

Управление в Прологе осуществляется с помощью предикатов, позволяющих изменять выполнение программы. К настоящему моменту мы познакомились с некоторыми средствами управления в Пролог-программе. К ним относятся:

  • fail – цель, которая всегда терпит неудачу;

  • true – цель, которая всегда успешна;

  • not(P) – отрицание, которое всегда ведет себя в точном соответствии со следующим определением: 

not(P) :- P, !, fail; true.

  • repeat – цель, которая всегда успешна. Она недетерминирована, поэтому, когда до нее доходит перебор, она порождает новую ветвь вычислений.

К другим средствам управления, позволяющим существенно влиять на вычислительный процесс, является использование некоторых стандартных предикатов. 1. Отсечение, обозначаемое восклицательным знаком («!»). Если в теле дизъюнкта встречается отсечение, то все утверждения между головой дизъюнкта и знаком «!» не могут пересогласовываться (переконкретизироваться). Перебор возможен только между головой дизъюнкта и знаком отсечения. Пример. Пусть имеются следующие описания. a(x):-b(X),!,c(X). a(x):-d(X). b(e). b(f). c(e). c(f). d(g). Если бы не было знака отсечения, на запрос a(Z) было бы выдано три ответа: Z=e,  Z=f,  Z=g. Из-за отсечения программа даст один ответ Z=e. Она не будет обращаться к разветвлениям, возможным после отсечения. Второе правило a(x) также игнорируется. Отсечение применяется в следующих основных случаях. 1) Прекращение бессмысленного перебора (бэктрекинга). Рассмотрим знакомый предикат принадлежности элемента списку. Первый клоз этого предиката можно записать в виде правила: member(X, [X| _ ]) :- ! . member(X, [ _ | T ]):– member(X,T). Как только будет найдено первое вхождение X в список, работа предиката прекращается, т.е. теперь процедура дает единственное решение. Мы повысили эффективность программы, оставив при этом ее декларативный смысл без изменения. Такое отсечение, не меняющее декларативный смысл программы, называется «зеленым отсечением22»

  • Отсечение как неуспех. Рассмотрим фразу: «Мэри любит всех животных, кроме змей». Как выразить это на Прологе?  Первую часть этого утверждения выразить легко: «Мэри любит всякого Х, если Х – животное»:

likes(“Мэри”, X) :- animal (X). Но нужно исключить змей. На Прологе сказать, что  что-либо не есть истина можно с помощью встроенного предиката fail(неуспех). Записанное далее отсечение предотвратит дальнейший перебор. Правило со змеей как исключительный случай, следует рассмотреть в первую очередь: likes(“Мэри”, X) :- animal (X), X = “змея”, ! , fail.  likes(“Мэри”, X) :- animal (X).  Здесь первое правило позаботится о змеях: если Х – змея, то отсечение предотвратит перебор, исключая таким образом второе правило из рассмотрения, а fail вызовет неуспех. Отсечение в этом примере меняет декларативный смысл программы и называется «красным отсечением»0,.    2. Собрать все решения в список. Эту работу выполняет высокоуровневый предикат findall: findall( Variable, Atom, ListVariable ) В списке ListVariable возвращаются все решения для переменной Variable предиката Atom. При помощи механизма перебора можно пучить все решения, удовлетворяющие некоторой цели. Однако всякий раз, когда порождается новое решение, предыдущее пропадает и с этого момента становится недоступным. Если хотим получить доступ ко всем порожденным объектам сразу, можно собрать их в список, что обеспечивает встроенный предикат  findall. Например, у нас есть предикаты-факты, представляющие некоторые результаты: result( “Иванов”, 370). result( “Петров”, 230). result( “Иванов”, 160). Чтобы получить список результатов, например, Иванова, зададим цель: Goal findall(R, result( “Иванов”, R), List), write(List). Полученный результат: List=[370,160]. Получить список всех фамилий можно целью: Goal findall(Fam, result( Fam, _ ), List), write(List). Список всех фамилий: List=[“Иванов”, “Петров”, “Иванов” ]. В список можно собирать практически объекты любого типа, включая сами же списки и другие структурные объекты Пролога. В программе только требуется объявить необходимую область определения Domains. Например, имеем предикаты: result( [10, 20, 30]). result( [40, 50]). Укажем область определения для предиката findall: Domains list = integer* Llist = list* и можно задавать цель  Goal findall(L, result( L ), L1), write(L1). В результате получим список списков L1=[ [10, 20, 30], [40, 50] ]. Предикат  findall является процедурой очень высокого уровня, поскольку выполняет много промежуточной работы. Его также можно использовать для запуска других предикатов. Допустим, предикат go(town1,town2,L) находит путь из одного города в другой. Чтобы найти все пути, ведущие из города town1 в town2, запустим цель: Goal findall(L, go( town1, town2, L ), L1), write(L1).