Когда Redo-порт вызывается с новыми переменными в Trace/0, а когда нет?

Во время моей реализации игры Adventure в Прологе мне было интересно, когда повторный порт вызывается с новыми переменными во время возврата и когда он вызывается с теми же самыми?

Например, у меня есть следующая база знаний:

location(desk,office).
location(apple,kitchen).
location(flashlight,desk).
location('washing machine',cellar).
location(nani,'washing machine').
location(broccoli,kitchen).
location(crackers,kitchen).
location(computer,office).

door(office,hall).
door(kitchen,office).
door(hall,'dining room').
door(kitchen,cellar).
door('dining room',kitchen).

При трассировке запроса ?-(location(X,Y),door(kitchen,Y)). Я получаю это:

   Call: (9) location(_7998, _8000) ? creep
   Exit: (9) location(desk, office) ? creep
   Call: (9) door(kitchen, office) ? creep
   Exit: (9) door(kitchen, office) ? creep
X = desk,
Y = office ;
   Redo: (9) door(kitchen, office) ? creep      <==== 1
   Fail: (9) door(kitchen, office) ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location(apple, kitchen) ? creep
   Call: (9) door(kitchen, kitchen) ? creep
   Fail: (9) door(kitchen, kitchen) ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location(flashlight, desk) ? creep
   Call: (9) door(kitchen, desk) ? creep
   Fail: (9) door(kitchen, desk) ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location('washing machine', cellar) ? creep
   Call: (9) door(kitchen, cellar) ? creep
   Exit: (9) door(kitchen, cellar) ? creep
X = 'washing machine',
Y = cellar ;
   Redo: (9) location(_7998, _8000) ? creep    <==== 2
   Exit: (9) location(nani, 'washing machine') ? creep
   Call: (9) door(kitchen, 'washing machine') ? creep
   Fail: (9) door(kitchen, 'washing machine') ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location(broccoli, kitchen) ? creep


   Call: (9) door(kitchen, kitchen) ? creep
   Fail: (9) door(kitchen, kitchen) ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location(crackers, kitchen) ? creep
   Call: (9) door(kitchen, kitchen) ? creep
   Fail: (9) door(kitchen, kitchen) ? creep
   Redo: (9) location(_7998, _8000) ? creep
   Exit: (9) location(computer, office) ? creep
   Call: (9) door(kitchen, office) ? creep
   Exit: (9) door(kitchen, office) ? creep
X = computer,
Y = office ;
   Redo: (9) door(kitchen, office) ? creep    <==== 3
   Fail: (9) door(kitchen, office) ? creep
false.

Я понимаю, как работает привязка переменных, но я не понимаю, почему Prolog возвращает

Redo: (9) door(kitchen, office) ? creep <==== 1
Redo: (9) door(kitchen, office) ? creep <==== 3

после нахождения первого решения (и того же самого для последнего решения),

в то время как он сразу ищет новое связывание X и Y после второго решения

Redo: (9) location(_7998, _8000) ? creep <==== 2

Почему он не сразу продолжает искать новое связывание X и Y после первого и последнего решения, вместо того чтобы прибегать к

Redo: (9) door(kitchen, office) ? creep <==== 3

(что все равно не получается)?

(Я нашел подобный вопрос здесь, но, к сожалению, моя репутация не позволяет мне комментировать там, и я думаю, что эта конкретная проблема не имеет отношения к моей версии Prolog (как я пробовал с SWI-Prolog и GNUProlog))

Заранее спасибо за предложение любой ясности.

2 ответа

Решение

Этот ответ использует SWI-Prolog.

Первое, что может помочь при первом использовании trace, - это сделать все порты видимыми. По умолчанию порт унификации не виден, но его можно сделать видимым с помощью

?- visible(+unify).

Теперь, поскольку это такой короткий запрос, вместо того, чтобы нажимать клавишу пробела для каждого порта, мы можем отключить привязку привязанных портов с помощью

?- leash(-call).
?- leash(-exit).
?- leash(-redo).
?- leash(-fail).

Теперь, если вы включите трассировку и запустите запрос

?- trace.
[trace] ?- (location(X,Y),door(kitchen,Y)).

вам не придется нажимать пробел, ожидайте ответов.

Сделав так вернется

   Call: (9) location(_9632, _9634)
   Unify: (9) location(desk, office)
   Exit: (9) location(desk, office)
   Call: (9) door(kitchen, office)
   Unify: (9) door(kitchen, office)
   Exit: (9) door(kitchen, office)
X = desk,
Y = office ;
   Redo: (9) door(kitchen, office)
   Fail: (9) door(kitchen, office)
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(apple, kitchen)
   Exit: (9) location(apple, kitchen)
   Call: (9) door(kitchen, kitchen)
   Fail: (9) door(kitchen, kitchen)
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(flashlight, desk)
   Exit: (9) location(flashlight, desk)
   Call: (9) door(kitchen, desk)
   Fail: (9) door(kitchen, desk)
   Redo: (9) location(_9632, _9634)
   Unify: (9) location('washing machine', cellar)
   Exit: (9) location('washing machine', cellar)
   Call: (9) door(kitchen, cellar)
   Unify: (9) door(kitchen, cellar)
   Exit: (9) door(kitchen, cellar)
X = 'washing machine',
Y = cellar ;
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(nani, 'washing machine')
   Exit: (9) location(nani, 'washing machine')
   Call: (9) door(kitchen, 'washing machine')
   Fail: (9) door(kitchen, 'washing machine')
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(broccoli, kitchen)
   Exit: (9) location(broccoli, kitchen)
   Call: (9) door(kitchen, kitchen)
   Fail: (9) door(kitchen, kitchen)
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(crackers, kitchen)
   Exit: (9) location(crackers, kitchen)
   Call: (9) door(kitchen, kitchen)
   Fail: (9) door(kitchen, kitchen)
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(computer, office)
   Exit: (9) location(computer, office)
   Call: (9) door(kitchen, office)
   Unify: (9) door(kitchen, office)
   Exit: (9) door(kitchen, office)
X = computer,
Y = office ;
   Redo: (9) door(kitchen, office)
   Fail: (9) door(kitchen, office)
false.

который теперь имеет видимые порты Unify.

Поскольку это такой короткий запрос, я прокомментирую важные строки трассировки, и это, в свою очередь, должно ответить на ваши вопросы.

   % location fact 1: location(desk, office) -------------------------
   % First predicate of query - location(X,Y)

   % The location facts are matched in the order of the source code
   % Since the code is looking for location(X,Y)
   % it matches location fact 1: location(desk, office)
   % Since there are more location facts like location(X, Y),
   % e.g.
   %   location(apple,kitchen).
   %   location(flashlight,desk).
   %   location('washing machine',cellar).
   %   location(nani,'washing machine').
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   % a choice point is generated
   % choice point: location 1
   Call: (9) location(_9632, _9634)

   % Unifies with first location fact.
   % X binds with desk
   % Y binds with office
   Unify: (9) location(desk, office)

   Exit: (9) location(desk, office)

   % Second predicate of query - door(kitchen,Y)).
   % Y is bound with office
   % The door facts are matched in the order of the source code
   % Since the code is only looking for door(kitchen,office)
   % it matches door fact 2: door(kitchen,office)
   % Since there are more door facts like door(kitchen,Y),
   % e.g.
   %    door(kitchen,cellar).
   % a choice point is generated
   % choice point: door 1
   Call: (9) door(kitchen, office)

   % Since there is a door(kitchen, office) fact
   % unify with second predicate
   Unify: (9) door(kitchen, office)
   Exit: (9) door(kitchen, office)
   % No more predicates in the query so return result.
X = desk,
Y = office ;

   % Remember choice point: door 1
   % Use the second predicate
   % on the remaining door facts like door(kitchen,Y)
   % e.g.
   %    door(kitchen,cellar).
   Redo: (9) door(kitchen, office)

   % There are no more door facts that unify with door(kitchen, office)
   % so fail.
   Fail: (9) door(kitchen, office)

   % Since the second predicate failed,
   % go back to the first predicate location(X,Y)
   % location fact 2: location(apple, kitchen) -----------------------

   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(apple,kitchen).
   %   location(flashlight,desk).
   %   location('washing machine',cellar).
   %   location(nani,'washing machine').
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)

   % The second fact unifies with the first predicate location(X,Y)
   % X binds with apple
   % Y binds with kitchen
   Unify: (9) location(apple, kitchen)
   Exit: (9) location(apple, kitchen)

   % Second predicate of query - door(kitchen,Y)).
   % Y is bound with kitchen
   % The door facts are matched in the order of the source code
   % Since the code is only looking for door(kitchen,kitchen)
   % it matches none of the door facts
   % and since it checked all of the door facts
   % no choice point was generated.
   Call: (9) door(kitchen, kitchen)

   % There is no door(kitchen, kitchen) fact so fail.
   Fail: (9) door(kitchen, kitchen)

   % location fact 3: location(flashlight, desk) ---------------------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(flashlight,desk).
   %   location('washing machine',cellar).
   %   location(nani,'washing machine').
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(flashlight, desk)
   Exit: (9) location(flashlight, desk)
   Call: (9) door(kitchen, desk)
   Fail: (9) door(kitchen, desk)

   % Since the second predicate failed,
   % go back to the first predicate location(X,Y)
   % location fact 4: location('washing machine', cellar)  -----------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location('washing machine',cellar).
   %   location(nani,'washing machine').
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)

   % The forth fact unifies with the first predicate location(X,Y)
   % X binds with 'washing machine'
   % Y binds with cellar
   Unify: (9) location('washing machine', cellar)

   Exit: (9) location('washing machine', cellar)

   % Second predicate of query - door(kitchen,Y)).
   % Y is bound with cellar
   % The door facts are matched in the order of the source code
   % Since the code is only looking for door(kitchen,cellar)
   % it matches door fact 4: door(kitchen,cellar)
   % Since there are NO more door facts like door(kitchen,Y),
   % NO choice point is generated
   Call: (9) door(kitchen, cellar)

   % There is a door(kitchen, cellar) fact so unify.
   Unify: (9) door(kitchen, cellar)
   Exit: (9) door(kitchen, cellar)
   % No more predicates in the query so return result.
X = 'washing machine',
Y = cellar ;

   % location fact 5: location(nani, 'washing machine') --------------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(nani,'washing machine').
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(nani, 'washing machine')
   Exit: (9) location(nani, 'washing machine')
   Call: (9) door(kitchen, 'washing machine')
   Fail: (9) door(kitchen, 'washing machine')

   % location fact 6: location(broccoli, kitchen) --------------------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(broccoli,kitchen).
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(broccoli, kitchen)
   Exit: (9) location(broccoli, kitchen)
   Call: (9) door(kitchen, kitchen)
   Fail: (9) door(kitchen, kitchen)

   % location fact 7: location(crackers, kitchen) --------------------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(crackers,kitchen).
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)
   Unify: (9) location(crackers, kitchen)
   Exit: (9) location(crackers, kitchen)
   Call: (9) door(kitchen, kitchen)
   Fail: (9) door(kitchen, kitchen)

   % Since the second predicate failed,
   % go back to the first predicate location(X,Y)
   % location fact 8: location(computer, office) ---------------------
   % Remember choice point: location 1
   % Use the first predicate
   % on the remaining location facts like location(X,Y)
   % e.g.
   %   location(computer,office).
   Redo: (9) location(_9632, _9634)

   % The last fact unifies with the first predicate location(X,Y)
   % X binds with computer
   % Y binds with office
   Unify: (9) location(computer, office)
   Exit: (9) location(computer, office)

   % Second predicate of query - door(kitchen,Y)).
   % Y is bound with office
   % The door facts are matched in the order of the source code
   % Since the code is only looking for door(kitchen,office)
   % it matches door fact 2: door(kitchen,office)
   % Since there are more door facts like door(kitchen,Y),
   % e.g.
   %    door(kitchen,cellar).
   % a choice point is generated
   % choice point: door 2
   Call: (9) door(kitchen, office)

   % Since there is a door(kitchen, office) fact
   % unify with second predicate
   Unify: (9) door(kitchen, office)
   Exit: (9) door(kitchen, office)
   % No more predicates in the query so return result.
X = computer,
Y = office ;
   % Remember choice point: door 2
   % Use the second predicate
   % on the remaining door facts like door(kitchen,Y)
   % e.g.
   %    door(kitchen,cellar).
   Redo: (9) door(kitchen, office)

   % There are no more door facts that unify with door(kitchen, office)
   % so fail.
   Fail: (9) door(kitchen, office)

   % There are no more location facts so end the query.
false.

дополнение

Когда я создавал ответ, он находился в текстовом редакторе, который может выделить синтаксис для кода Пролога, и чтобы помочь мне разобраться, у меня было три открытых панели для сравнения. На одной панели было расположение и правила дверей. На одной панели был комментарий, а на третьей - аналогичный комментарий. По мере продвижения по коду я продолжал обновлять комментарии на третьей панели, чтобы они соответствовали аналогичным комментариям на второй панели, одновременно проверяя факты на первой панели. Я отмечаю это, потому что это, вероятно, лучший способ понять комментарии, чем читать их из этого ответа в опубликованном виде.

Редактор - Visual Studio Code, и для выделения Prolog установлено расширение Prolog.

Это просто комментарий, опубликованный как ответ, потому что у него есть картинка.

Если вы используете графический отладчик, вы можете видеть, когда создаются точки выбора.

?- gtrace.
true.

[trace] ?- (location(X,Y),door(kitchen,Y)).

На следующем изображении отладчика я выделил точку выбора зеленым прямоугольником.

Для выхода из режима трассировки на верхнем уровне введите nodebug,

[trace] ?- nodebug.
true.

?- 

Примечание: это точки выбора, которые приводят к использованию порта повтора.
Примечание. Другой способ отладки пролога - использование фрагмента сбоя. Также прочитайте вопросы, помеченные срезом

Другие вопросы по тегам