我正在学习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
所以我如何理解这一点,我每次都通过在休息中保存它来循环移除头部,这样我就可以在之后重新附加它。
我接近这个错误吗?
答案 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,就可以拆分列表,这样你就可以使用Before
和After
这样的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).