List Prolog-如何获取列表的和';s元素?

List Prolog-如何获取列表的和';s元素?,list,prolog,List,Prolog,我对prolog非常陌生,我正在尝试编写一个小程序,给定一个列表,返回列表元素的总和。根据我看到的所有示例,我找到了如下解决方案: addup([],0). addup([FirstNumber | RestOfList], Total) :- addup(RestOfList, TotalOfRest), Total is FirstNumber + TotalOfRest. sum([], 0). sum([H|T], N):- X is H+N, sum(

我对prolog非常陌生,我正在尝试编写一个小程序,给定一个列表,返回列表元素的总和。根据我看到的所有示例,我找到了如下解决方案:

addup([],0).
addup([FirstNumber | RestOfList], Total) :-
    addup(RestOfList, TotalOfRest),
    Total is FirstNumber + TotalOfRest.
sum([], 0).
sum([H|T], N):-
    X is H+N,
    sum(T, X).
但当我用这些值测试该解决方案时:

?- addup([1,2,3,4],0).
我只是从中得到垃圾值,比如_34521

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

addup([],0).
addup([FirstNumber | RestOfList], Total) :-
    addup(RestOfList, TotalOfRest),
    Total is FirstNumber + TotalOfRest.
sum([], 0).
sum([H|T], N):-
    X is H+N,
    sum(T, X).
这一个能够正确地将数字相加,但它无法将X的最终值(即真实答案)传递到N中。因此对于测试用例:

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

我得到的答案是6,最终值是N,第二个倒数第二个值是X,而不是10,最终值是X。如果您能提供任何帮助和/或解释为什么这两种解决方案都不起作用,我们将不胜感激。谢谢。

首先,空列表的总和是0-这是基本情况,您已经有了

其次,元素
H
和列表
T
的和
N
是添加到
H
的列表
T
的和。Prolog是在统一的基础上运行的,因此这表示
N
H
T
之和是统一的,其中
T
的和是使用
sum(T,X)
X
统一的

这使得:

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

更多信息:

\u 34521
是一个未绑定变量的表示-这表示存在一个变量,但它尚未与值统一

次要考虑事项:

对于一个适当长的列表,上面的实现将耗尽堆栈空间,因为递归的每个帧都必须被存储,这样加法就可以自下而上进行。为了防止堆栈错误,我们可以使用尾部递归累加器,如下所示:

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

sum([],N,N).
sum([H|T],A,N) :-
    A1 is A + H,
    sum(T,A1,N).
。。。它保留原始方法的签名,即包装总和/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
尾部递归:

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是10^7,length(L,N),maplist(=(1),L),sum(L,N)。
(由@false引发)很可能会在没有尾部递归的情况下失败,并且很可能会成功。

什么不是数字?哇,这只是我从其他地方复制的一个示例,所以我知道如何格式化测试用例。现在已修复显示正确的函数名。谢谢。您的第一个解决方案
addup/2
运行良好
addup([1,2,3,4],S)。
成功并生成
S=10
addup([1,2,3,4],0)。
表示“否”或“假”-即失败(换句话说,总和不是0)。所以我不知道问题出在哪里。请注意,第二个参数表示总和
addup/2
是一个Prolog谓词,谓词不返回值。他们成功或失败(或他们没有终止)。哦!谢谢你,我现在明白了!然而,
N是10^7,length(L,N),maplist(=(1),L),sum(L,N)。
产生一个错误-它应该会成功。@JimAshworth:是的,它只是关于可能是常数的资源消耗。最好是使用clpfd的版本。@false-true,但这是一个足够简单的问题,clpfd对于一个只接触Prolog的人来说感觉太过分了(我在2014年的一门大学课程中学习了Prolog,大约一周前才发现了clpfd)@Jim:不需要自信!试试看!