Prolog中列表中的元素总和几乎无需解释

时间:2013-11-21 18:45:00

标签: prolog

我是prolog编程的初学者,我希望你谦虚并帮助我解决这个困惑

我在prolog中计算总和面临一个问题,我有答案,但对我来说并不是那么清楚。 答案是:

list_sum([], 0).
list_sum([Head | Tail], Total) :-
   list_sum(Tail, Sum1),
   Total = Head + Sum1.

我不明白的是Sum1是什么以及程序如何按步骤工作

首先检查第一个条件list_sum([], 0).,但条件不满足时会将列表分为2个部分HeadTail然后呢?

我希望你接受一个小小的初学者并给他一些时间来纠正他的困惑。
谢谢你们

2 个答案:

答案 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:... SET ...

  • :- ...然后我们可以得出结论......

  • list_sum([E|Es], S)S是列表[E|Es]的总和。

通过这种方式,可以理解这些事情,而不必过多地参考程序细节。理解终止还有更多内容,请参阅

答案 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开始获取每个先前级别计算的值。最终,调用链到达顶层,将最终结果绑定到调用者传递的变量。