我是prolog编程的初学者,我希望你谦虚并帮助我解决这个困惑
我在prolog中计算总和面临一个问题,我有答案,但对我来说并不是那么清楚。 答案是:
list_sum([], 0).
list_sum([Head | Tail], Total) :-
list_sum(Tail, Sum1),
Total = Head + Sum1.
我不明白的是Sum1
是什么以及程序如何按步骤工作
首先检查第一个条件list_sum([], 0).
,但条件不满足时会将列表分为2个部分Head
和Tail
然后呢?
我希望你接受一个小小的初学者并给他一些时间来纠正他的困惑。
谢谢你们
答案 0 :(得分:5)
程序中有一个小错误 - 行Total = Head + Sum
表示Total
是带有两个参数的结构+
。您可能意味着is
代替=
意味着算术评估。但最好是使用#=
代替。
在您的问题中,您要求该程序将执行什么。这在面向命令(“命令式”)语言中是一个非常合理的问题,因为你可以从程序中获得的唯一含义是它的逐步操作。但在Prolog中,事情有点不同。您仍然可以尝试逐步思考,但迟早您会意识到事情会变得非常复杂,仅仅因为Prolog没有一个控制流,而是两个同时被调用(AND-和OR-控制)。甚至“数据结构”也不同......
但是有一种方法可以阅读Prolog程序,它在命令式语言中没有对应程序:您可以将程序理解为参数之间的关系。通过这种方式,您可以专注于关系的样子而不是程序方面。毕竟,如果描述的关系是错误的,那么询问程序如何做到这一点毫无意义。
所以让我改写一下你的节目:
:- use_module(library(clpfd)).
list_sum([], 0).
list_sum([E|Es], S) :-
S #= E+T,
list_sum(Es, T).
首先会检查第一个条件list_sum([],0)。如果条件不满足,它会将列表分为H和T两部分吗?
你的问题暗示有一个单一的控制流程(“虽然这是一个典型的构造,暗示它”)。但它也可能有所不同。考虑一下查询:
?- list_sum(Xs,0).
Xs = [] ;
Xs = [0] ;
Xs = [_G1710, _G1713],
_G1710+_G1713#=0 ...
这里我们要求存在哪些列表,其总和为0 。现在,你的“同时”不再有意义。
我们得到的答案是:空列表;包含0
的列表;一个两元素列表,其中元素之和为0;等...
了解此类程序的最佳方法是将它们视为关系:
list_sum([], 0
:空列表的总和为0 。
现在,规则在箭头:-
的方向上是最好的,即从右到左:
list_sum(Es, T).
:*如果Es
是一个总和为T
且<...
S #= E+T
:... S
为E
加T
...
:-
...然后我们可以得出结论......
list_sum([E|Es], S)
:S
是列表[E|Es]
的总和。
通过这种方式,可以理解这些事情,而不必过多地参考程序细节。理解终止还有更多内容,请参阅failure-slice。
答案 1 :(得分:4)
这是经典的递归方法 - 您需要熟悉它才能理解Prolog。
您的规则有两个子句 - 一个用于空列表,另一个用于非空列表。空列表子句表示空列表的元素总和为零(这是完全合理的)。这被称为“递归的基本情况”。每个终止递归规则都必须有一个基本情况。
第二个条款有点复杂。它粗略地说:“计算非空列表中元素的总和,首先砍掉初始元素,然后计算得到的较短列表中元素的总和。调用求和Sum1
。现在计算Total
通过将初始元素的值添加到Sum1
的值。
第二个子句递归地将列表分解为一系列较短的列表,直到它们到达空列表。此时第一个子句介入,提供空列表的总和。
考虑这个例子:
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
这是有效的,因为递归链中的每个调用级别都为Sum1
获取自己的变量。这些值开始无限制,但是一旦递归调用链“触底”,Sum1
开始获取每个先前级别计算的值。最终,调用链到达顶层,将最终结果绑定到调用者传递的变量。