【问题标题】:Prolog: compare list elements and sumProlog:比较列表元素和总和
【发布时间】:2018-11-13 19:08:19
【问题描述】:

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.

据我了解,该错误是由于基本情况所致(它有空列表,导致评估加法时出错?)

感谢您的帮助和解释!

【问题讨论】:

  • 您的递归 predicate(不是 function)子句由于匿名变量而基本上“丢弃”了每个列表的头部 (_) .你想要fn([X|Xs], [Y|Ys], [Z|Zs]) :- ...Z is X + Y 的条件。您还只需要对fn(Xs, Ys, Zs) 进行一次递归调用。
  • N1 is T1+T2.... 这里,T1T2 是列表。您不能算术添加列表。 + 只对数字起作用。

标签: list prolog


【解决方案1】:

除了@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 库,您可能会觉得有趣。

【讨论】:

    【解决方案2】:

    据我了解,错误是由于基本情况造成的。

    我不这么看。

    我看到的第一个问题是您正在尝试处理导致考虑使用 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 的条件或其他一些返回真的条件。一开始我倾向于忽略这些,并专注于先完成另一部分,在这种情况下elements of third list is sum of the two lists 然后我检查其他条件是否正确。对于大多数简单的课堂练习,它们都是如此。我有时认为老师试图给出这些额外的条件来迷惑学生,但实际上只是为了澄清代码应该如何工作。

    【讨论】:

    • 您的第三个代码块中有一个错字引起了您的注意:应该是 fn([H1|T1],[H2|T2],[H3|T3]) 而不是 fn([H1,T1],[H2,T2],[H3,T3])
    • @tas 是的,谢谢。
    • @tas 我认为我在这里写的任何内容都是抄送的,所以请随时编辑我的任何问题或答案。
    • 很高兴知道。我通常很犹豫编辑其他人的答案,但如果你不介意,如果我发现它们,我将在以后编辑明显的事故:-)
    • 感谢您的详尽解释。一个后续问题:假设我想进行以下查询:fn([A|B],[C|D],[4,4]).。我想获取所有整数对 A+B=4 和 C+D=4 的列表,我该怎么做?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多