Prolog谓词将检查列表A是否是列表D的前缀和的列表

时间:2017-12-12 07:57:26

标签: list prolog clpfd

所以我对prolog很新,当整数列表D是列表A的前缀和列表时,必须编写一个可满足的谓词。

sums(A, D)

例如,

sums([4,11,1,-3,8], [4,15,16,13,21]) is satisfiable

我已经用十几种不同的方式写了这个谓词无济于事。这就是我目前所写的内容。

sums([], []).
sums([A], [A]).
sums([A|[B|C]], [A|[E|F]]) :- TOTAL is A + B, E = TOTAL, sums([C], [F]).

这有点起作用,因为它将检查每个列表的第一个值是否相等,并且还检查列表中的第二个元素是否正确,因为它应该是15.我理解为什么它以这种方式不正确地工作,但我无法以正确的方式提出不同的写法。

我已将代码更改为

sumrunner(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A], [A], _).
sumrunner([A|B], [C|D], TOTAL) :- TOTAL is TOTAL + A, TOTAL = C,sumrunner(B, D, TOTAL).

然而,现在它只是对所有情况都说假,除了两个列表都是空的,并且列表都包含一个元素并且它们彼此相等时。

2 个答案:

答案 0 :(得分:3)

您应该详细了解列表符号:例如,[A|[B|C]]可以写为[A,B|C]。现在更清楚的是C是列表的尾部,因此,它本身就是一个列表!因此,当您编写sums([C], [F])时,您将CF包装到列表中,即使它们已经是列表,这也是您的问题。

如果我们修复此问题并运行您的谓词,我们就会得到:

?- sums([4,11,1,-3,8],Z).
Z = [4, 15, 1, -2, 8] 

你可以看到它仍然是错误的。主要问题是,第三条规则中的递归调用sums表示列表尾部的前缀和是列表前缀和的尾部,这是错误的,因为这些前缀和取决于上一个元素!

要解决这个问题,您需要引入一个额外的参数来维持递归调用的总和值:

:- use_module(library(clpfd)).

prefix_sums(L, D) :-
    prefix_sums(L, 0, D).

prefix_sums([], _, []).
prefix_sums([H|T], S, [S1|T2]) :-
    S1 #= H + S,
    prefix_sums(T, S1, T2).

使用library(clpfd),我们得到了我们期望的行为:

?- prefix_sums([4,11,1,-3,8],Z).
Z = [4, 15, 16, 13, 21].

但也有相反的行为:

?- prefix_sums(Z, [4,15,16,13,21]).
Z = [4, 11, 1, -3, 8].

并且用更少的信息来纠正行为:

?- prefix_sums([A,B,C],Z).
Z = [A, _7964, _7970],
B+A#=_7964,
C+_7964#=_7970.

?- prefix_sums(X,Z).
X = Z, Z = [] ;
X = Z, Z = [_7122],
_7122 in inf..sup ;
X = [_7452, _7458],
Z = [_7452, _7482],
_7458+_7452#=_7482 ;
X = [_7770, _7776, _7782],
Z = [_7770, _7806, _7812],
_7776+_7770#=_7806,
_7782+_7806#=_7812 ;
…

答案 1 :(得分:2)

您的代码必须经过简化:

sums(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A|B], [C|D], TOTAL) :- C is TOTAL + A, sumrunner(B, D, C).

?- sums([4,11,1,-3,8], [4,15,16,13,21]).
true.

?- sums([4,11,1,-3,8], [4,15,16,14,21]).
false.

表达式C is TOTAL + A都检查需求并更新累加器以进行递归步骤。