SWI-Prolog:findall / 3找不到所有解决方案?

时间:2016-05-02 14:40:34

标签: prolog transitive-closure

我正在尝试解决此Pebble Solitaire问题,这是我的代码的一部分:

% 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)时,游戏只是45和111的任何序列(例如[45,111,45,45,45,45,111,111,111,45,45, 45]),值只与2个项目的列表统一(编辑:不是,它与更多项目统一,请参阅注释)。

根据我的理解,当我调用findall / 3时,它在基本情况谓词中找到一个解决方案(它只计算鹅卵石的数量并用X统一它),然后从另外20个中的任何一个解决方案玩谓词,然后......停止?

我需要它继续前进,直到找到所有解决方案。为什么它在2个解决方案后停止?我怎样才能继续?

1 个答案:

答案 0 :(得分:3)

您的计划存在一些问题。你找到了一个。还有更多!

1mo作为惯例,省略属于同一谓词的子句之间的空行。这有助于避免您遇到的这些意外问题。

2do另一个有用的约定是避免使用ASCII码。而是使用chars(长度为1的原子),如下所示:

[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))! (首先,只需将SWI的double_quotes.pl下载到与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----"
...

这些是许多中间步骤。是否会有多少或无限多?会有周期吗?我们可以盯着所有的答案,或让Prolog为我们思考:

?- 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.

让我重复一遍:这里,Prolog证明了所有可能有9个孔的电路板没有周期。当然有一个更简单的论点:一个移动移除一个卵石,另一个移动将鹅卵石移动到右边。但最后一个问题的要点是:Prolog确实通过立即考虑所有可能的主板 来证明这一点!