SWI-Prolog: findall/3 не находит все решения?
Я пытаюсь решить эту проблему пасьянс Pebble, и это часть моего кода:
% Base case
play(List, X) :-
count_pebbles(List, X).
%%%%%%%%%%%%%%
% JUMP RIGHT %
%%%%%%%%%%%%%%
% oo-XXXXXXXXX
play( [111, 111, 45|Tail], X) :-
play([45, 45, 111|Tail], X).
% Xoo-XXXXXXXX
play( [A, 111, 111, 45|Tail], X) :-
play([A, 45, 45, 111|Tail], X).
% XXoo-XXXXXXX
play( [A, B, 111, 111, 45|Tail], X) :-
play([A, B, 45, 45, 111|Tail], X).
% XXXoo-XXXXXX
play( [A, B, C, 111, 111, 45|Tail], X) :-
play([A, B, C, 45, 45, 111|Tail], X).
% XXXXoo-XXXXX
play( [A, B, C, D, 111, 111, 45|Tail], X) :-
play([A, B, C, D, 45, 45, 111|Tail], X).
% XXXXXoo-XXXX
play( [A, B, C, D, E, 111, 111, 45|Tail], X) :-
play([A, B, C, D, E, 45, 45, 111|Tail], X).
% XXXXXXoo-XXX
play( [A, B, C, D, E, F, 111, 111, 45|Tail], X) :-
play([A, B, C, D, E, F, 45, 45, 111|Tail], X).
% XXXXXXXoo-XX
play( [A, B, C, D, E, F, G, 111, 111, 45|Tail], X) :-
play([A, B, C, D, E, F, G, 45, 45, 111|Tail], X).
% XXXXXXXXoo-X
play( [A, B, C, D, E, F, G, H, 111, 111, 45|Tail], X) :-
play([A, B, C, D, E, F, G, H, 45, 45, 111|Tail], X).
% XXXXXXXXXoo-
play( [A, B, C, D, E, F, G, H, I, 111, 111, 45], X) :-
play([A, B, C, D, E, F, G, H, I, 45, 45, 111], X).
%%%%%%%%%%%%%
% JUMP LEFT %
%%%%%%%%%%%%%
% -ooXXXXXXXXX
play( [45, 111, 111|Tail]) :-
play([111, 45, 45|Tail]).
% X-ooXXXXXXXX
play( [A, 45, 111, 111|Tail]) :-
play([A, 111, 45, 45|Tail]).
% XX-ooXXXXXXX
play( [A, B, 45, 111, 111|Tail]) :-
play([A, B, 111, 45, 45|Tail]).
% XXX-ooXXXXXX
play( [A, B, C, 45, 111, 111|Tail]) :-
play([A, B, C, 111, 45, 45|Tail]).
% XXXX-ooXXXXX
play( [A, B, C, D, 45, 111, 111|Tail]) :-
play([A, B, C, D, 111, 45, 45|Tail]).
% XXXXX-ooXXXX
play( [A, B, C, D, E, 45, 111, 111|Tail]) :-
play([A, B, C, D, E, 111, 45, 45|Tail]).
% XXXXXX-ooXXX
play( [A, B, C, D, E, F, 45, 111, 111|Tail]) :-
play([A, B, C, D, E, F, 111, 45, 45|Tail]).
% XXXXXXX-ooXX
play( [A, B, C, D, E, F, G, 45, 111, 111|Tail]) :-
play([A, B, C, D, E, F, G, 111, 45, 45|Tail]).
% XXXXXXXX-ooX
play( [A, B, C, D, E, F, G, H, 45, 111, 111|Tail]) :-
play([A, B, C, D, E, F, G, H, 111, 45, 45|Tail]).
% XXXXXXXXX-oo
play( [A, B, C, D, E, F, G, H, I, 45, 111, 111]) :-
play([A, B, C, D, E, F, G, H, I, 111, 45, 45]).
Да, это ужасно
Тем не менее, когда я звоню findall( Value, play(Game, Value), Values)
где Game - это любая последовательность из 45 и 111 (например, [45, 111, 45, 45, 45, 45, 111, 111, 111, 45, 45, 45]), значения только когда-либо объединяются со списком 2 элемента (РЕДАКТИРОВАТЬ: не соответствует действительности, объединяет больше элементов, см. Комментарии).
Из того, что я понимаю, когда я вызываю findall/3, он находит одно решение в предикате базового случая (которое просто подсчитывает количество камешков и объединяет его с X), а затем одно решение из любого из 20 других предикатов воспроизведения, а потом просто... останавливается?
Мне нужно, чтобы оно продолжалось до тех пор, пока не будут найдены все решения. Почему это останавливается после 2 решений? Как я могу заставить это продолжаться?
1 ответ
Есть несколько проблем с вашей программой. Вы нашли один. Есть еще!
1mo Как правило, опускайте пустые строки между предложениями, которые принадлежат одному и тому же предикату. Это помогает избежать таких случайных проблем, которые у вас были.
2do Еще одно полезное соглашение - избегать использования ASCII-кодов. Вместо этого используйте символы (атомы длины один), например, так:
[45,111,45,45,45,45,111,111,111,45,45,45] % your example
[-,o,-,-,-,-,o,o,o,-,-,-] % using chars
"-o----ooo---" % :- set_prolog_flag(double_quotes, chars).
Обратите внимание, что директива set_prolog_flag(double_quotes, chars)
влияет только на способ двойных кавычек "strings"
читаются Он по-прежнему выбрасывает фактические символы для замены ответа:
?- L = "-o--".
L = [-,o,-,-].
Чтобы получить также компактные ответы используйте use_module(library(double_quotes))
! (Для начала просто скачать double_quotes.pl
для SWI в ту же директорию, что и ваш файл Prolog, и скажите):
?- use_module(double_quotes).
true.
?- L = "-o--".
L = "-o--".
3tio Вот переписать: сначала разделить ваш предикат play/2
в одиночные ходы. Смешивание рекурсии с другим кодом часто довольно громоздко. Представьте, рекурсивный предикат не завершится! Во всяком случае, слишком много в то же время. Вот мой взгляд на это:
:- set_prolog_flag(double_quotes,chars).
move(Xs0, Xs) :-
phrase( ( seq(Prefix), localmove ) , Xs0, Xs1),
phrase( seq(Prefix), Xs, Xs1).
localmove, "o--" --> "-oo".
localmove, "-oo" --> "oo-".
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
?- move("-o----ooo---",S).
S = "-o---o--o---" ;
S = "-o----o-oo--" ;
false.
Так что теперь у нас есть только один ход. Далее мы хотим несколько ходов в последовательности. Чтобы быть уверенным, что мы не зацикливаемся, я буду использовать closure0/3
определено в другом примере.
?- S0 = "-o----ooo---", closure0(move, S0,S).
S0 = "-o----ooo---", S = S0
; S0 = "-o----ooo---", S = "-o---o--o---"
; S0 = "-o----ooo---", S = "-o----o-oo--"
; S0 = "-o----ooo---", S = "-o----oo----"
...
Это много промежуточных шагов. Будет ли бесконечно много или бесконечно много? И будут ли циклы? Мы можем смотреть на все ответы или позволить Прологу думать за нас:
?- S0 = "-o----ooo---", move(S0,S1), closure0(move, S1,S0).
% one move ahead, more moves, ^^ and back
false.
Это терпит неудачу, таким образом, нет никаких циклов назад к исходному состоянию. Можем ли мы доказать это в целом? Для всех возможных длин? Вплоть до N = 9
Я получил сбои, как и ожидалось:
?- length(S0,9), move(S0,S1), closure0(move,S1,S0).
false.
Позвольте мне просто повторить это: здесь Пролог доказал, что все возможные доски с 9 лунками не имеют циклов. Конечно, есть более простой аргумент: одно движение удаляет гальку, другое движение перемещает гальку вправо. Но смысл последнего запроса: Пролог доказал это, рассмотрев все возможные доски одновременно!