Пролог не дает мне решения, когда он существует
Я работаю через Семь языков за семь недель, но есть кое-что, чего я не понимаю в прологе. У меня есть следующая программа (в зависимости от их программы Wallace и Grommit):
/* teams.pl */
onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).
teamMate(X, Y) :- \+(X = Y), onTeam(X, Z), onTeam(Y, Z).
и загрузить это так
?- ['teams.pl'].
true.
но это не дает мне никаких решений для следующего
?- teamMate(a, X).
false.
это может решить более простые вещи (что показано в книге):
?- onTeam(b, X).
X = aTeam ;
X = superTeam.
и есть решения:
?- teamMate(a, b).
true ;
false.
Что мне не хватает? Я пробовал как с GNU пролог и Swipl.
... И ЕЩЕ БОЛЬШЕ...
когда вы перемещаете ограничение "не может быть вашим собственным товарищем по команде", то оно заканчивается:
/* teams.pl */
onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).
teamMate(X, Y) :- onTeam(X, Z), onTeam(Y, Z), \+(X = Y).
это дает мне решения, которые я ожидал:
?- ['teams.pl'].
true.
?- teamMate(a, X).
X = b.
?- teamMate(b, X).
X = a ;
X = c.
Что дает?
2 ответа
Ответ от mat дает вам некоторые соображения высокого уровня и решение. Мой ответ больше о причинах, которые могут или не могут быть вам интересны.
(Кстати, изучая Пролог, я задал почти тот же вопрос и получил очень похожий ответ от того же пользователя. Отлично.)
Дерево доказательства
У вас есть вопрос:
Товарищи по команде двух игроков?
Чтобы получить ответ от Пролога, вы сформулируете запрос:
?- team_mate(X, Y).
где X и Y могут быть свободными переменными или связанными.
Основываясь на вашей базе данных о предикатах (фактах и правилах), Пролог пытается найти доказательства и предлагает вам решения. Пролог ищет доказательства, выполняя обход дерева доказательств в глубину.
В вашей первой реализации, \+ (X = Y)
предшествует чему-либо еще, поэтому он находится в корневом узле дерева и будет оцениваться до достижения следующих целей. И если либо X
или же Y
свободная переменная, X = Y
должен преуспеть, что означает, что \+ (X = Y)
должен потерпеть неудачу. Таким образом, запрос должен потерпеть неудачу.
С другой стороны, если либо X
или же Y
свободная переменная, dif(X, Y)
будет успешным, но более поздняя попытка объединить их друг с другом не удастся. В этот момент Прологу придется искать доказательство в другой ветви дерева доказательств, если они еще остались.
(Имея в виду дерево доказательств, попробуйте найти способ реализации dif/2
Вы думаете, что это возможно без а) добавления какого-либо состояния к аргументам dif/2
или б) изменение стратегии разрешения?)
И, наконец, если вы положите \+ (X = Y)
в самом конце, и позаботьтесь о том, чтобы оба X
а также Y
к моменту его оценки, тогда объединение становится больше похожим на простое сравнение, и оно может потерпеть неудачу, так что отрицание может быть успешным.
Вы сделали очень хорошее наблюдение! На самом деле ситуация еще хуже, потому что даже самый общий запрос не выполняется:
? - teamMate (X, Y). ложь
Декларативно, это означает, что "решений вообще не существует", что, очевидно, неверно, а не то, как мы ожидаем, что отношения будут вести себя: если есть решения, то более общие запросы не должны завершаться неудачей.
Причина, по которой вы получаете это странное и логически некорректное поведение, заключается в том, что (\+)/1
Это только звук, если его аргументы достаточно созданы.
Чтобы выразить неравенство терминов в более общем виде, который работает правильно, независимо от того, созданы ли аргументы или нет, используйте dif/2
или, если ваша система Prolog не обеспечивает этого, безопасное приближение iso_dif/2
который вы можете найти в теге prolog-dif.
Например, в вашем случае (note_that_I_am_using_underscores_for_readability
вместо tuckingTheNamesTogetherWhichMakesThemHarderToRead
):
team_mate (X, Y): - dif (X, Y), on_team (X, Z), on_team (Y, Z).
Ваш запрос теперь работает точно так, как ожидалось:
? - team_mate (a, X). Х = б.
Конечно, самый общий запрос также работает правильно:
? - team_mate (X, Y). Х = а, Y = b; X = b, Y = а; X = b, Y = с; и т.п.
Таким образом, используя dif/2
выражать неравенство сохраняет логическую чистоту ваших отношений: система больше не просто говорит false
хотя есть решения. Вместо этого вы получите ожидаемый ответ! Обратите внимание, что, в отличие от ранее, это также работает независимо от того, где вы звоните!