Prolog:比较列表元素和总和

时间:2018-11-13 19:08:20

标签: list prolog

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.

据我了解,该错误是由于基本情况造成的(它具有空列表,这会导致加法评估出错?)

感谢您的帮助和解释!

2 个答案:

答案 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] ? ;
.
.
.

还有一点需要注意:您可能会发现有趣的布尔值(CLP(B)),有理数和实数(CLP(Q,R))的clp库。

答案 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然后检查其他条件是否正确。对于大多数简单的课堂练习,它们都是如此。我有时认为老师会尝试给出这些额外的条件来使学生感到困惑,但实际上,仅仅是为了阐明代码应如何工作。