例如:
列表[1,2,3,4,5,6],N等于6应打印 true ,因为正好有3个值加起来为6. 1 + 2 + 3。< / p>
N等于12的列表[2,5,7,9]应打印 false ,因为没有3个元素加起来为12。
答案 0 :(得分:2)
我们可以从更一般的谓词开始,该谓词描述列表,所述列表的子列表和子列表中的数字之和之间的关系。为了明确哪个参数是什么,为谓词选择描述性名称是合适的,比如说sum_ofsub_fromlist/3
。现在让我们观察如果第一个参数是子列表中数字的总和,则从该和中连续地减去这些数字会产生零,例如:X = A + B→X-A-B = 0。因此,将有一个基本案例包含0
作为和[]
作为子列表(规则1)和递归规则,从总和中减去子列表的元素(规则2)。并且由于子列表不包含从一般情况下取得的列表的所有元素,因此将存在用于跳过列表中未出现在子列表中的元素的递归规则(规则3)。只要子列表中仍有元素,只需要此规则,因此约束将是有益的,一旦子列表为空,就会阻止此规则成功。这些想法可以像Prolog一样在Prolog中实现:
sum_ofsub_fromlist(0,[],_L). % rule 1
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :- % rule 2
X0 is X-A,
sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :- % rule 3
dif(Bs,[]), % constraint: sublist not empty
sum_ofsub_fromlist(X,Bs,As).
您可以查询此谓词以确保自己在示例中提供给定总和的所有子列表:
?- sum_ofsub_fromlist(6,S,[1,2,3,4,5,6]).
S = [1, 2, 3] ;
S = [1, 5] ;
S = [2, 4] ;
S = [6] ;
false.
?- sum_ofsub_fromlist(12,S,[2,5,7,9]).
S = [5, 7] ;
false.
在此基础上,您可以编写一个仅对长度为3的子列表成功的调用谓词:
sum_oftriple_fromlist(S,T,L) :-
T=[_,_,_], % T has to be a triple
sum_ofsub_fromlist(S,T,L).
这个谓词产生了你想要的答案:
?- sum_oftriple_fromlist(6,T,[1,2,3,4,5,6]).
T = [1, 2, 3] ;
false.
?- sum_oftriple_fromlist(12,T,[2,5,7,9]).
false.
请注意,谓词也使用负数:
?- sum_oftriple_fromlist(6,T,[-5,-3,-1,2,4,7,8,9]).
T = [-5, 2, 9] ;
T = [-5, 4, 7] ;
T = [-3, 2, 7] ;
false.
?- sum_oftriple_fromlist(-6,T,[-6,-5,-4,-3,-2,-1,2,4]).
T = [-6, -4, 4] ;
T = [-6, -2, 2] ;
T = [-5, -3, 2] ;
T = [-3, -2, -1] ;
false.
但是,由于使用了is/2
,谓词只有在第一个和第三个参数被接地时才有效:
?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
ERROR: is/2: Arguments are not sufficiently instantiated
Exception: (7) sum_ofsub_fromlist(_G918, [_G1016, _G1019, _G1022], [1, 2, 3, 4, 5, 6]) ?
?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
ERROR: is/2: Arguments are not sufficiently instantiated
Exception: (7) sum_ofsub_fromlist(6, [_G2121, _G2124, _G2127], [_G1945, _G1948, _G1951, _G1954, _G1957, _G1960]) ?
如果这对您没问题,您可以在这里停下来。或者,您可以选择使用CLP(FD)使谓词更具通用性。只需对代码应用这些小修改:
:- use_module(library(clpfd)). % <- new
sum_oftriple_fromlist(S,T,L) :-
T=[_,_,_],
sum_ofsub_fromlist(S,T,L).
sum_ofsub_fromlist(0,[],_L).
sum_ofsub_fromlist(X,[A|Bs],[A|As]) :-
X0 #= X-A, % <- change
sum_ofsub_fromlist(X0,Bs,As).
sum_ofsub_fromlist(X,Bs,[_A|As]) :-
dif(Bs,[]),
sum_ofsub_fromlist(X,Bs,As).
现在上述查询提供了答案:
?- sum_oftriple_fromlist(S,T,[1,2,3,4,5,6]).
S = 6,
T = [1, 2, 3] ;
S = 7,
T = [1, 2, 4] ;
S = 8,
T = [1, 2, 5] ;
. % another
. % seventeen
. % results here
然而,第二个查询产生了剩余目标(详见documentation)结果:
?- sum_oftriple_fromlist(6,T,[A,B,C,D,E,F]).
T = [A, B, C],
_G2424+A#=6,
C+B#=_G2424 ;
T = [A, B, D],
_G2424+A#=6,
D+B#=_G2424 ;
.
.
.
要获得实际数字,您必须限制数字的范围,然后在列表中标记变量:
?- L=[A,B,C,D,E,F], sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 1, 4, 1, 1, 1],
A = B, B = D, D = E, E = F, F = 1,
C = 4,
T = [1, 1, 4] ;
L = [1, 1, 4, 1, 1, 2],
A = B, B = D, D = E, E = 1,
C = 4,
F = 2,
T = [1, 1, 4] ;
.
.
.
可能您只对每个号码只出现一次的列表感兴趣:
?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(6,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = 6,
T = [1, 2, 3] ;
L = [1, 2, 3, 4, 6, 5],
A = 1,
B = 2,
C = 3,
D = 4,
E = 6,
F = 5,
T = [1, 2, 3] ;
.
.
.
或许你甚至不想限制总和:
?- L=[A,B,C,D,E,F], all_distinct(L), sum_oftriple_fromlist(S,T,L), L ins 1..6, label(L).
L = [1, 2, 3, 4, 5, 6],
A = 1,
B = 2,
C = 3,
D = 4,
E = 5,
F = S, S = 6, % sum = 6
T = [1, 2, 3] ;
.
.
.
L = [1, 2, 4, 3, 5, 6],
A = 1,
B = 2,
C = 4,
D = 3,
E = 5,
F = 6,
S = 7, % sum = 7
T = [1, 2, 4] ;
.
.
.
正如您所看到的,谓词的CLP(FD)版本类似于真正的关系而非非CLP(FD)版本。当然,您的示例查询会在两个版本中产生相同的答案。
答案 1 :(得分:0)
您的代码仅考虑列表中的前3项,而不考虑任何其他组合。
构建涉及列表的解决方案的最自然的方法是将递归基于列表的结构。所以:
请注意,您可能需要谓词的“帮助”版本,以允许您添加其他参数。在这种情况下,知道需要添加多少个值会有所帮助。