Вопрос

Я читаю "Семь языков за семь недель" в банкомате, и я в тупике из-за какого-то запроса 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.Почему?

Это было полезно?

Решение

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

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.

Другие советы

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top