Проблема с ` ` в запросах Prolog с переменными

#prolog #predicate #gnu-prolog

#prolog #предикат #gnu-prolog

Вопрос:

Я читаю «Семь языков за семь недель» в банкомате, и я в тупике из-за какого-то запроса Prolog, на который я не понимаю ответа «нет».

friends.pl Файл выглядит следующим образом:

 likes(wallace, cheese).
likes(grommit, cheese).
likes(wendolene, sheep).

friend(X, Y) :-  (X = Y), likes(X, Z), likes(Y, Z).
  

Я могу выполнить несколько тривиальных запросов к нему, таких как:

 | ?- ['friends'].
compiling /home/marc/btlang-code/code/prolog/friends.pl for byte code...
/home/marc/btlang-code/code/prolog/friends.pl compiled, 12 lines read - 994 bytes written, 8 ms

yes
| ?- friend(wallace,grommit).

yes
| ?- friend(wallace,wendolene).

no
  

Все так, как ожидалось. Теперь я хочу ввести переменную в запрос. Я намереваюсь, чтобы Prolog предоставил мне список всех друзей Уоллеса. Я ожидаю X = grommit , но получаю no :

 | ?- trace.
The debugger will first creep -- showing everything (trace)

yes
{trace}
| ?- friend(wallace,X).
      1    1  Call: friend(wallace,_16) ?
      2    2  Call:  wallace=_16 ?
      3    3  Call: wallace=_16 ?
      3    3  Exit: wallace=wallace ?
      2    2  Fail:  wallace=_16 ?
      1    1  Fail: friend(wallace,_16) ?

no
{trace}
  

Он даже не пытается объединить X ( _16 ) с grommit . Почему?

Ответ №1:

Это определение друга:

 friend(X, Y) :-  (X = Y), likes(X, Z), likes(Y, Z).
  

Здесь важно то, что вы начинаете с (X = Y) , который обычно определяется как:

   Goal :- Goal,!,fail
  

Обратите внимание, что это означает, что если цель будет успешной, вы наверняка потерпите неудачу. Свободные переменные (те, которые не были назначены) всегда будут объединены и, следовательно, равны, поэтому вы всегда будете терпеть неудачу со свободной переменной. Таким образом, он никогда не присвоит значение X или Y, если у него его еще нет.

Вместо

 friend(X, Y) :-  likes(X, Z), likes(Y, Z),  (X = Y)
  

будет вести себя больше так, как вы ожидаете.

Проблема здесь в том, что prolog предоставляет вам мощные способы управления потоком программ, но они не очень хорошо вписываются в его более логически ориентированный дизайн. Должна быть возможность выразить ограничения типа «отрицание как сбой» таким образом, чтобы не создавать этих проблем. По этой причине я не большой поклонник prolog.

Ответ №2:

Что касается комментария Филиппа Дж.Ф. выше:

Должна быть возможность выразить ограничения типа «отрицание как сбой» таким образом, чтобы не создавать этих проблем.

Это возможно: современным решением для таких проблем являются ограничения. В этом случае используйте, например dif/2 , доступный во всех серьезных системах Prolog.

Комментарии:

1. gprolog (версия 3): uncaught exception: error(existence_error(procedure,dif/2),friend/2)

2. Попробуйте, например, SWI, Yap, SICStus, B-Prolog и несколько других.

Ответ №3:

Первой подцелью friend/2 является (X = Y) . Это выполняется путем первой попытки найти решение для X = Y , затем отрицания этого результата. Предикат =/2 примерно эквивалентен unify/2 , то есть он пытается объединить левый операнд с правым операндом. Теперь, когда вы задаете запросы, используя, например, friend(wallace, gromit) , два атома wallace и gromit не объединяются; но когда в микс добавляется свободная переменная, она всегда объединяется с любым заданным термином, поэтому X = Y всегда выполняется успешно, следовательно (X = Y) , всегда завершается неудачей, и выполнение никогда не проходит мимо этой первой подцели.

Комментарии:

1. Хорошо, понял. Как бы вы переформулировали friend/2 , чтобы это работало с запросом, содержащим переменные?

2. На первый взгляд, я бы использовал оператор равенства ==/2 и переписал его как friend(X, Y) :- (X == Y), likes(X, Z), likes(Y, Z). но в этой версии есть недостаток, заключающийся в том, что вы включаете себя в список своих друзей, например, в качестве решения friend(wallace, X) будет список wallace . Так что лучше, как предлагает Филип Дж.Ф., даже если он все еще использует оператор объединения, поставить проверку в конце: friend(X, Y) :- likes(X, Z), likes(Y, Z), X == Y . (Обратите внимание, что ==/2 это эквивалентно отрицанию ==/2 , но понятнее, просто более прямолинейно.)

Ответ №4:

Другая проблема, связанная с введением ограничения неравенства в первую очередь, заключается в том, что невозможно найти привязку для несвязанного X (исключая на данный момент тривиальный случай объединения его с grommit). Prolog находит привязки, просматривая свою базу данных, пытаясь объединить несвязанную переменную. Вот почему likes(grommit, Z) будет найдена некоторая привязка для Z , которая затем может быть дополнительно обработана, потому что в базе данных есть likes предложения. Но нет таких предложений для неравенства grommit с чем-либо, поэтому Prolog не может создавать какие-либо привязки. При существующем положении вещей friend предикат должен убедиться, что все переменные связаны, прежде чем неравенство может быть проверено.