Prolog中列表中元素的总和不需要太多解释

Prolog中列表中元素的总和不需要太多解释,prolog,failure-slice,Prolog,Failure Slice,我是prolog编程的初学者,我希望你们能谦虚地帮助我克服这一困惑 我在prolog中面临一个计算和的问题,我有答案,但我不太清楚。 答案是: list_sum([], 0). list_sum([Head | Tail], Total) :- list_sum(Tail, Sum1), Total = Head + Sum1. 我不明白的是什么是Sum1,以及程序如何分步工作 它首先将检查第一个条件列表_sum([],0)。当不满足该条件时,它将列表分为两部分头部和尾部 我希望你

我是prolog编程的初学者,我希望你们能谦虚地帮助我克服这一困惑

我在prolog中面临一个计算和的问题,我有答案,但我不太清楚。 答案是:

list_sum([], 0).
list_sum([Head | Tail], Total) :-
   list_sum(Tail, Sum1),
   Total = Head + Sum1.
我不明白的是什么是
Sum1
,以及程序如何分步工作

它首先将检查第一个条件<代码>列表_sum([],0)。当不满足该条件时,它将列表分为两部分<代码>头部和<代码>尾部


我希望你能接受一个初学者,给他一些时间来纠正他的困惑。

谢谢大家

这是经典的递归方法-您需要熟悉它才能理解Prolog

您的规则有两个子句-一个用于空列表,另一个用于非空列表。empty list子句表示空列表的元素之和为零(这是完全合理的)。这被称为“递归的基本情况”。每个终止递归规则都必须有一个基本情况

第二条稍微复杂一点。它大致是这样说的:“要计算非空列表中的元素之和,首先切掉初始元素,然后计算得到的较短列表中的元素之和。调用该和
Sum1
。现在通过将初始元素的值与
Sum1
的值相加来计算
Total

第二个子句递归地将列表分解为一系列较短的列表,直到得到一个空列表。此时,第一个子句介入,提供一个空列表的总和

考虑这个例子:

list_sum([12, 34, 56], X)
    list_sum([34, 56], <unknown-1>)
        list_sum([56], <unknown-2>)
            list_sum([], 0)         ---> succeeds with Total bound to 0
        <unknown-2> becomes 0 + 56  ---> succeeds with Total bound to 56
    <unknown-1> becomes 0 + 56 + 34 ---> succeeds with Total bound to 90
X becomes 0 + 56 + 34 + 12          ---> succeeds with X bound to 102
list_sum([12,34,56],X)
列表总和([34,56],)
列表和([56],)
list_sum([],0)-->成功,总计绑定到0
变为0+56--->成功,总绑定到56
变为0+56+34--->成功,总绑定为90
X变为0+56+34+12--->成功,X绑定到102

这是因为递归链中的每个调用级别都会为
Sum1
获取自己的变量。这些值从无界开始,但一旦递归调用链“见底”“,
Sum1
s开始获取前一级计算的值。最终,调用链到达顶层,将最终结果绑定到调用者传递的变量。

程序中有一个小错误-行
Total=Head+Sum
表示
Total
是一个带有两个参数的结构
+
。你可能是说
而不是
=
意味着算术计算。但最好是使用
#=
代替

在你的问题中,你是在问这个程序将做什么。在面向命令的(“命令式”)语言中,这是一个相当合理的问题,因为您可以从程序中获得的唯一含义是它的逐步操作。但在序言中,情况有点不同。您仍然可以尝试一步一步地进行思考,但迟早您会意识到,这里的事情可能变得非常复杂,这仅仅是因为Prolog没有一个控制流,而是同时调用了两个控制流(AND-AND或control)。甚至“数据结构”也是不同的

但是有一种方法可以读取Prolog程序,它在命令式语言中没有对应的语言:您可以将程序理解为参数之间的关系。通过这种方式,您可以将注意力集中在关系的外观上,而不是程序方面。毕竟,如果所描述的关系是错误的,那么询问程序如何做到这一点是没有意义的

因此,让我重新表述一下您的计划:

:- use_module(library(clpfd)).
list_sum([], 0).
list_sum([E|Es], S) :-
   S #= E+T,
   list_sum(Es, T).
它首先将检查第一个条件列表_sum([],0)。当条件不满足时,它会将列表分为两部分H和T,然后

您的问题暗示存在单个控制流(“while是暗示它的典型构造”)。但它的工作方式也可能有所不同。考虑查询:

?- list_sum(Xs,0).
Xs = [] ;
Xs = [0] ;
Xs = [_G1710, _G1713],
_G1710+_G1713#=0 ...
在这里,我们询问存在哪些总和为0的列表。现在你的“while”不再有意义了

我们得到的答案是:空名单;带有
0
的列表;两个元素的列表,其中元素的总和为0;等等

理解此类程序的最佳方法是将其视为如下关系:

list\u sum([],0
:空列表的sum为0

现在,该规则在箭头
:-
的方向上读取效果最好,即从右到左:

  • list\u sum(Es,T)。
    :*提供的
    Es
    是一个包含sum
    T

  • S#=E+T
    :…
    S
    E
    T

  • :-
    …那么我们可以得出结论

  • list|u sum([E|Es],S)
    S
    是列表的总和
    [E|Es]


通过这种方式,这些事情不需要太多的程序细节就可以理解。更重要的是要理解终止协议。请看。

非常感谢。非常感谢。上帝保佑你。我最初很难理解这一点。但你只是简单地为我解释清楚,但是“返回102”等等,有点误导,特别是对于习惯于按程序思考的人来说。序言子句不返回值;它们根本不“返回”。更准确的说法是“成功地使用
X
绑定到102”,等等。@TedHopp我也考虑过这一点-返回有点误导。我补充道:“使用,试图暗示该值在过程意义上没有“返回”。不过,我更喜欢你的措辞,-感谢你的精彩评论!+1,我强烈推荐这种风格用于读取谓词,并使用有限域约束进行整数运算。