繁体   English   中英

Prolog-如何获取列表元素的总和?

[英]Prolog - How to get the sum of a list's elements?

我对序言很陌生,我正在尝试编写一个小程序,给定一个列表,该程序返回列表元素的总和。 遵循我所看到的所有示例,使我想到了一个类似于以下的解决方案:

addup([],0).
addup([FirstNumber | RestOfList], Total) :-
    addup(RestOfList, TotalOfRest),
    Total is FirstNumber + TotalOfRest.

但是,当我使用以下值测试该解决方案时:

?- addup([1,2,3,4],0).

我只是从中获得_34521之类的垃圾值。

然后,我尝试了第二个解决方案,如下所示:

sum([], 0).
sum([H|T], N):-
    X is H+N,
    sum(T, X).

这个可以正确地将数字相加,但是它无法将X的最终值(是正确的答案)传递给N。因此对于测试用例:

?- sum([1,2,3,4], 0).

我得到的答案是6(N的最终值)和X的倒数第二个值,而不是X(X的最终值),而不是10。有关这些解决方案都不起作用的任何帮助和/或解释都将非常有用赞赏。 谢谢。

首先,一个空列表的总和为0-这是您已经拥有的基本情况。

其次,元素H与列表T的总和N是添加到H的列表T的总和。 序言操作上的统一,所以这表示, N是统一的总和HT ,其中的总和T是统一与X使用sum(T,X)

sum([], 0).
sum([H|T], N):-
    sum(T, X),
    N is X + H.

这给出:

?- sum([1,2,3,4],N).
N = 10.

在您最初的问题中, ?- sum([1,2,3,4], 0). 实际上说“如果列表[1,2,3,4]的总和为0这是正确的”-您需要传递变量作为第二个参数,以使sum与答案统一。

更多信息:

_34521是未绑定变量的表示-表示存在一个变量,但尚未将其与值统一。

次要考虑因素:

对于适当的长列表,上面的实现将用完堆栈空间,因为必须存储递归的每一帧,以便从下往上进行添加。 为了防止出现堆栈错误,我们可以像这样使用尾递归累加器:

sum(L, N):-
    sum(L, 0, N).

sum([],N,N).
sum([H|T],A,N) :-
    A1 is A + H,
    sum(T,A1,N).

...保留原始方法的签名,包装sum / 3。 由于规则主体中没有选择点(假设A + H对于给定的A和H始终相同,并且列表为空或不为空),因此Prolog会在离开范围时丢弃每个框架(因为没有更多操作可做),完成后将返回到原始调用者。

每个实例中sum([1,2,3],N)简化堆栈:

非尾递归:

rest-of-stack
rest-of-stack,sum([1|[2,3]],_1001)
rest-of-stack,sum([1|[2,3]],_1001),sum([2|[3]],_1002)
rest-of-stack,sum([1|[2,3]],_1001),sum([2|[3]],_1002),sum([3|[]],_1003)
rest-of-stack,sum([1|[2,3]],_1001),sum([2|[3]],_1002),sum([3|[]],_1003),sum([],0)
rest-of-stack,sum([1|[2,3]],_1001),sum([2|[3]],_1002),sum([3|[]],3)
rest-of-stack,sum([1|[2,3]],_1001),sum([2|[3]],5)
rest-of-stack,sum([1|[2,3]],6)
rest-of-stack

尾递归:

rest-of-stack
rest-of-stack,sum([1,2,3],_1001)
rest-of-stack,sum([1|[2,3]],0,_1001)
rest-of-stack,sum([2|[3]],1,_1001)
rest-of-stack,sum([3|[]],3,_1001)
rest-of-stack,sum([],6,6)
rest-of-stack

请注意,尾递归堆栈的深度对于列表的任何长度都是有限的(取决于调用它的名称),其中非尾递归堆栈的深度与列表的长度成正比。

因此,诸如N is 10^7,length(L,N),maplist(=(1),L),sum(L,N).的呼叫N is 10^7,length(L,N),maplist(=(1),L),sum(L,N). (由@false引发)可能会在没有尾递归的情况下失败,并且很可能会成功。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM