Prolog 下一个谓词混淆

Prolog 下一个谓词混淆,prolog,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_firs

我正在学习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,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,0]

[1,0,0,1,1,1,0]->[1,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]

所以我是如何理解这一点的,每次我都会循环移除头部,将其保存在Rest中,这样我就可以在之后将其追加回去


我处理这个问题的方法是错误的吗?

处理这个问题的一种逻辑方法是:找出在1之前的内容,然后找出在1之后和0之前的内容,然后休息。然后,需要交换1和0,以便在0之前、0之前、1之后、0之后都有1

从小处做起。首先,要在有1时拆分列表,以便在之前有
,在
之后有
,您可以使用示例中的列表,像这样使用
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种解决方案。现在,您需要在
之后查看
内部,以查看
1
的放置位置--好的,您需要将其放置在第一个0之后。因此,让我们在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
,前0之后的最后一个片段称为
After0
。您只需将它们重新组合在一起:
之前
,0,
之前
,1,
之后
。您可以使用2参数
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.
(为了节省空间,我只保留了
结果
绑定。)


我想你已经完成了。

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

基本情况很简单。它表示最小的有效移动

move([1,0], [0,1]).
然后是递归情况。由于基本情况已经处理了2个位置的琐碎情况,因此它们在列表中强制执行至少3个位置,并在规则中建立互斥以避免冗余解决方案

% 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
您还可以确定哪些起始位置将导致特定的下一个位置:

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

谢谢你的回答,我就是这样做的:

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

关于你的第二个问题:你在两个子句中都添加了
0
——只要在第一个子句中将其更改为
1
,它就会按预期工作。啊,我是个白痴哈,谢谢@StevenI还有一个问题,不一定需要回答,只是我很好奇。如果这是一个不同的问题,要么(a)你想要一个答案,你应该把它作为stackoverflow的一个单独的问题来问,要么(b)如果你不想要答案,问它会让当前的问题有点混乱。好的,不用担心,我会去掉第二部分,例如[1,0,0,0,1,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]@Loverergreat示例。所以每个解都是一个1的单步移动。为什么不编辑您的问题并在那里添加示例?然后其他人可以很容易地看到。干得好s(X)。我仍然无法理解转换应该做什么:-/@潜伏者它应该教学生一些东西,大概是如何使用Prolog;-)说真的,这个问题经常出现。我认为这是关于在游戏中寻找下一个可能的状态。不确定游戏规则是什么,但我猜列表是一个(一维)棋盘,0是空白点,1是棋盘上的东西,“下一步”移动只是根据问题中描述的规则移动一个东西。是的,我得到了一般要点。不过,由于某种原因,我一直无法理解搬家的具体规则。。。例如,移动单个1的规则是什么,导致
[1,0,0,1,1,1,0]
变成
[1,0,0,1,1,1]
?“我一定有心理障碍。”潜伏者我的理解是,一个东西向右移动到第一个空位。在您给出的示例中,位置4中的1移动到第一个自由点,即位置7。@潜伏者。。。这也是为什么
[0,1]
没有可能的移动的原因。例如,这更简洁,但不像我提供的解决方案那样通用。例如,假设您想问“什么起始位置导致移动
[1,0,0,1,0,1,1]
?”。如果使用我提供的解决方案,您可以查询
移动[1,0,0,1,0,1,1])
并获得正确的结果。在这种情况下,您的解决方案产生的结果是不正确的,可能是因为依赖于cut(
)使其简洁。