下一个谓词混淆

时间:2017-05-25 00:05:49

标签: prolog

我正在学习prolog,我目前正在为一款非常简单的游戏编写下一个谓词。

你有一个列表[1,0,1,0,0,1]合法移动是将1移动到零位置,1只能向右移动到包含0的第一个位置,但允许如有必要,跳过其他值。

首先我编写了一个谓词来将值0更改为1:

flip_first_zero([0|Tail], [1|Tail]). 

很简单,现在我试图找到合法的举动,我会尝试解释我的思考过程:

next([],[],[]).
next([Head|Tail], Rest, Out):-
    flip_first_zero([Head|Tail],List1),
    append(Rest,List1,Out).

next([Head|Tail], [Head|Rest], List):-
    next(Tail,Rest, List).

示例[1,0,0,1,1,1,0]输出应为[0,1,0,1,1,1,0] ; [1,0,0,0,1,1,1]; [1,0,0,1,0,1,1] ; [1,0,0,1,1,0,1].

[ 1 ,0,0,1,1,1,0] - > [0, 1 下,0,1,1,1,0]

[1,0,0, 1 ,1,1,0] - > [1,0,0,0,1,1, 1

[1,0,0,1, 1 ,1,0] - > [1,0,0,1,0,1, 1

[1,0,0,1,1, 1 ,0] - > [1,0,0,1,1,0, 1

所以我如何理解这一点,我每次都通过在休息中保存它来循环移除头部,这样我就可以在之后重新附加它。

我接近这个错误吗?

3 个答案:

答案 0 :(得分:3)

问题有两个部分:(1)1的位置,(2)找到1后的第一个0的位置,这样1可以放在新的位置。 Prolog解决方案将反映这一点。您的初始尝试尝试处理单个谓词中的所有内容,我相信这会使任务变得更加困难。

基本案例很简单。它代表了最小的有效行动。

move([1,0], [0,1]).

然后是递归案例。这些在列表中至少强制执行3个职位,因为基本案例已经处理了2个职位的微不足道的情况,并且在规则中建立互斥以避免冗余解决方案。

% If we're at a 0 (space), keep going
move([0,X1,X2|T], [0|R]) :-
    move([X1,X2|T], R).

% If we see a 1, we can move it, or we can leave it alone and move on
move([1,X1,X2|T], [0|R]) :-
    place([X1,X2|T], R).
move([1,X1,X2|T], [1|R]) :-
    move([X1,X2|T], R).

% Place the 1 at the first located 0 (space)
place([0|T], [1|T]).
place([1|T], [1|R]) :-
    place(T, R).

因此,要从起始位置确定有效的下一个位置:

| ?-  move([1,0,0,1,1,1,0],R).

R = [0,1,0,1,1,1,0] ? a

R = [1,0,0,0,1,1,1]

R = [1,0,0,1,0,1,1]

R = [1,0,0,1,1,0,1]

(1 ms) no
| ?-

您还可以确定哪些起始位置将导致特定的下一个位置:

| ?- move(S, [1,0,0,1,0,1,1]).

S = [1,0,0,1,1,0,1] ? a

S = [1,0,0,1,1,1,0]

S = [1,0,1,0,0,1,1]

no

<小时/> 这也可以使用DCG来完成:

move([0, 1]) --> [1, 0].
move([0|R]) --> see(0), move(R).
move([0|R]) --> see(1), place(R).
move([1|R]) --> see(1), move(R).

see(N), [X1, X2] --> [N, X1, X2].

place([1|T]) --> [0], seq(T).
place([1|R]) --> [1], place(R).

seq([]) --> [].
seq([X|Xs]) --> [X], seq(Xs).

| ?- phrase(move(R), [1,0,0,1,1,1,0]).

R = [0,1,0,1,1,1,0] ? a

R = [1,0,0,0,1,1,1]

R = [1,0,0,1,0,1,1]

R = [1,0,0,1,1,0,1]

no
| ?- phrase(move([1,0,0,1,0,1,1]), S).

S = [1,0,0,1,1,0,1] ? a

S = [1,0,0,1,1,1,0]

S = [1,0,1,0,0,1,1]

no

答案 1 :(得分:2)

解决这个问题的一种合乎逻辑的方法:找到1之前的内容,然后是1之后和0之前的内容,以及休息之后的内容。然后,您需要交换1和0,以便在0,1之前,0之后的1之前,0之后。

从小处开始。首先,只要你有1,就可以拆分列表,这样你就可以使用BeforeAfter这样的append/3,使用你的例子中的列表:

?- append(Before, [1|After], [1,0,0,1,1,1,0]).
Before = [],
After = [0, 0, 1, 1, 1, 0] ;
Before = [1, 0, 0],
After = [1, 1, 0] ;
Before = [1, 0, 0, 1],
After = [1, 0] ;
Before = [1, 0, 0, 1, 1],
After = [0] ;
false.

您已经获得了预期的4种解决方案。现在你需要查看After内部以查看放置1的位置 - 好吧,你需要把它放在第一个0之后。所以让我们将After拆分为0,但是只有一次:

?- append(Before, [1|After], [1,0,0,1,1,1,0]),
   once( append(Before0, [0|After0], After) ). 
Before = Before0, Before0 = [],
After = [0, 0, 1, 1, 1, 0],
After0 = [0, 1, 1, 1, 0] ;
Before = [1, 0, 0],
After = [1, 1, 0],
Before0 = [1, 1],
After0 = [] ;
Before = [1, 0, 0, 1],
After = [1, 0],
Before0 = [1],
After0 = [] ;
Before = [1, 0, 0, 1, 1],
After = [0],
Before0 = After0, After0 = [] ;
false.

现在你有3件:一件在1之前称为Before,一件在1和第一件0之间称为Before0,最后一件之后称为After0。您只需将它们重新组合在一起:Before,0,Before0,1,After0。您可以使用带有第一个参数中列表列表的双参数append/2

?- append(Before, [1|After], [1,0,0,1,1,1,0]),
   once( append(Before0, [0|After0], After) ),
   append([Before, [0|Before0], [1|After0]], Result).
Result = [0, 1, 0, 1, 1, 1, 0] ;
Result = [1, 0, 0, 0, 1, 1, 1] ;
Result = [1, 0, 0, 1, 0, 1, 1] ;
Result = [1, 0, 0, 1, 1, 0, 1] ;
false.

(我只留下Result个绑定来节省空间。)

我认为你已经完成了。

答案 2 :(得分:-1)

感谢您的所有答案,我就这样做了:

flip_first_zero([0|L],[1|L]):-!.
flip_first_zero([X|L],[X|L1]):-
    flip_first_zero(L,L1).

next([1|L],[0|L1]):-
    flip_first_zero(L,L1).
next([X|L],[X|L1]):-
    next(L,L1).