prolog的新功能,并尝试实现以下包含3个列表的功能:
示例:fn([1,2,3],[4,5,6],[5,7,9])
返回true
。请注意,总和是逐元素加法。
这是我到目前为止所拥有的:
fn([],[],[]).
fn([_|T1], [_|T2], [_|T3]) :-
fn(T1,T2,T3), % check they are same length
fn(T1,T2,N1), % check that T3=T1+T2
N1 is T1+T2,
N1 = T3.
据我了解,该错误是由于基本情况造成的(它具有空列表,这会导致加法评估出错?)
感谢您的帮助和解释!
答案 0 :(得分:3)
除了@GuyCoder的答案,我还指出,在修改列表的所有元素时,值得考虑使用来自library(apply)的maplist谓词之一。您可以使用谓词来描述三个数字之间的关系...
:- use_module(library(apply)). % for maplist/4
num_num_sum(X,Y,S) :-
S is X+Y.
...,然后使用maplist / 4将其应用于整个列表:
fn(X,Y,Z) :-
maplist(num_num_sum,X,Y,Z).
如果前两个列表被完全实例化,则该谓词将产生所需的结果:
?- fn([1,2,3],[4,5,6],X).
X = [5,7,9]
但是,由于使用is / 2,如果前两个列表包含变量,则会出现实例化错误:
?- fn([1,A,3],[4,5,6],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is _+B: expected bound value
?- fn([1,2,3],[4,5,A],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is A+B: expected bound value
如果只想对整数列表使用谓词,则可以使用CLP(FD)使其更具通用性:
:- use_module(library(apply)).
:- use_module(library(clpfd)). % <- use CLP(FD)
int_int_sum(X,Y,S) :-
S #= X+Y. % use CLP(FD) constraint #=/2 instead of is/2
fnCLP(X,Y,Z) :-
maplist(int_int_sum,X,Y,Z).
使用此定义,以前有问题的查询也可以工作:
?- fnCLP([1,A,3],[4,5,6],[5,7,9]).
A = 2
?- fnCLP([1,2,3],[4,5,A],[5,7,9]).
A = 6
即使是最一般的查询,使用此版本也会产生结果:
?- fnCLP(X,Y,Z).
X = Y = Z = [] ? ;
X = [_A],
Y = [_B],
Z = [_C],
_A+_B#=_C ? ;
X = [_A,_B],
Y = [_C,_D],
Z = [_E,_F],
_A+_C#=_E,
_B+_D#=_F ? ;
.
.
.
由于上述答案中的数字不是唯一确定的,因此您得到的是剩余目标,而不是实际数字。为了获得答案中的实际数字,您必须限制两个列表的范围并随后对其进行标记(有关详细信息,请参见documentation),例如要生成包含第一个列表中的数字3,4,5和第二个列表中的数字6,7,8的列表,您可以查询:
label the lists
restrict the domain | |
v v v v
?- fnCLP(X,Y,Z), X ins 3..5, Y ins 6..8, label(X), label(Y).
X = Y = Z = [] ? ;
X = [3],
Y = [6],
Z = [9] ? ;
X = [3],
Y = [7],
Z = [10] ? ;
.
.
.
X = [3,4],
Y = [6,7],
Z = [9,11] ? ;
X = [3,4],
Y = [6,8],
Z = [9,12] ? ;
.
.
.
答案 1 :(得分:1)
据我了解,该错误是由于基本情况造成的。
我不这样认为。
我看到的第一个问题是您正在尝试处理列表,从而导致考虑使用DCG,但是由于您是新手,所以我会避免使用该路由。
在处理列表时,通常会处理列表的开头,然后使用递归将尾部传递回谓词。
例如要列出的长度
ln([],N,N).
ln([_|T],N0,N) :-
N1 is N0+1,
ln(T,N1,N).
ln(L,N) :-
ln(L,0,N).
谓词ln/2
用于设置初始计数0,而谓词ln/3
使用递归进行工作。请注意,列表的开头如何从列表的前面移开,列表的结尾如何又递归地传递到谓词上。当列表为空时,谓词ln([],N,N).
将在第二个位置到第三个位置的中间计数(在本例中认为是副本)统一起来,然后将其与ln/2
一起传回。
现在回到您的问题。
基本情况很好
fn([],[],[]).
一共有三个列表,每个列表都以[H|T]
的形式查看
fn([H1|T1],[H2|T2],[H3|T3])
而在尾部进行递归的调用是
fn(T1,T2,T3)
剩下的就是处理头部了
H3 is H1 + H2
把它们放在一起给我们
fn([],[],[]).
fn([H1|T1], [H2|T2], [H3|T3]) :-
H3 is H1 + H2,
fn(T1,T2,T3).
和一些检查。
?- fn([],[],[]).
true.
?- fn([1],[1],[2]).
true.
?- fn([1,2],[3,4],[4,6]).
true.
?- fn([1,2],[3,4,5],[4,6,5]).
false.
关于两个条件。当我看逻辑编程练习题时,它们有时会给出诸如True if lists are the same length
之类的条件或其他返回true的条件。我倾向于一开始不理会这些,而是专注于先完成另一部分,在这种情况下,elements of third list is sum of the two lists
然后检查其他条件是否正确。对于大多数简单的课堂练习,它们都是如此。我有时认为老师会尝试给出这些额外的条件来使学生感到困惑,但实际上,仅仅是为了阐明代码应如何工作。