prolog - 检查给定列表是否与模式匹配并替换某些元素

时间:2017-03-26 07:46:35

标签: list prolog

我遇到以下prolog代码的问题:

valid(X, Y) :-
    append([E1 | T2], [E1 | T3], X1), append([E1|T1], X1, X),
    append([E2 | T2], [E2 | T3], Y1), append([E2|T1], Y1, Y),
    E2 is E1 + 1.

我要做的是:对于给定的列表X,检查它是否与模式[E1, ..., E1, ..., E1, ...]匹配;如果是,请将所有E1替换为E1+1...部分中可以包含任意数量的元素,即T1T2T3可以是任意长度。

例如,我的预期输出是:

?- valid([1,5,1,6,7,1,8], Y).
Y = [2, 5, 2, 6, 7, 2, 8] ;
false.

?- valid([1,5,6,1,7,1], Y).
Y = [2, 5, 6, 2, 7, 2] ;
false.

?- valid([1,5,6,7,8], Y).
false.

但现在我得到的实际输出是:

?- valid([1,5,1,6,7,1,8], Y).
Y = [2, 5, 2, 6, 7, 2, 8] ;
ERROR: Out of global stack

?- valid([1,5,6,7,8], Y).
ERROR: Out of global stack

问题是,prolog将尝试尝试无限长度的T1T2T3,因此永远不会停止。因此,当我的输入列表X与模式不匹配时,它会一直运行直到内存空间不足。

有没有办法修复我的代码,以便在找到所有可能的答案(可能不止一个)或X与模式不匹配时停止?

谢谢!

1 个答案:

答案 0 :(得分:2)

Append/3谓词不是坚定的,所以它在开始时评估它的参数。所以你需要在前面移动评估X的规则,如:

valid(X, Y) :-
    append([E1|T1], X1, X),append([E1 | T2], [E1 | T3], X1),
    append([E2|T1], Y1, Y),append([E2 | T2], [E2 | T3], Y1),
    E2 is E1 + 1.

由于X被评估,因此第一次附加不会导致尝试无限长度,并且类似地第二次追加。

一些例子:

?- valid([1,5,1,6,7,1,8], Y).
Y = [2, 5, 2, 6, 7, 2, 8] ;
false.

?- valid([1,5,6,1,7,1], Y).
Y = [2, 5, 6, 2, 7, 2] ;
false.

?- valid([1,5,6,7,8], Y).
false. 

我更倾向于使用一些简单的递归和计算E1的外观来解决问题(并在输出列表中替换为E2),因为追加可能有点棘手......

例如:

:- use_module(library(clpfd)).

valid([H|T], [H2|T2]):-  H2 #= H+1 ,valid(H,T,T2,1).

valid(_,[],[],3).
valid(H,[H|T1],[H2|T2],Count):- H2 #= H+1, Count1 #= Count+1,valid(H,T1,T2,Count1).  
valid(H,[H1|T1],[H1|T2],Count):- dif(H1,H), valid(H,T1,T2,Count).  

还有一些例子:

?- valid([1,5,1,6,7,1,8], Y).
Y = [2, 5, 2, 6, 7, 2, 8] ;
false.

?- valid([1,5,6,1,7,1], Y).
Y = [2, 5, 6, 2, 7, 2] ;
false.

?- valid([1,5,6,7,8], Y).
false.

?- valid(X, [2, 5, 2, 6, 7, 2, 8]).
X = [1, 5, 1, 6, 7, 1, 8] ;
false.

使用CLPFD库使谓词更具关系性,正如您在上面的上一个示例中所看到的,您可以在不能使用之前进行查询:valid(X, [2, 5, 2, 6, 7, 2, 8]).(使用以前的解决方案)。

这是另一种替代方式:

new(H,H,R):- R is H+1.
new(H,N,N):- dif(N,H). 

valid([H|T], Y):- findall(X1, (member(X1,[H|T]),X1 = H) , L), 
                  length(L,3), maplist(new(H),[H|T],Y).