如何不使用列表对谓词值求和?

Lel*_*uch 2 prolog swi-prolog

我试图不使用列表来总结谓词的值。对值求和,最后得到这些值的总和。

%shop(TicketNumber, Client, Month).
shop(1, ash, 12).
shop(2, nelson, 11).
shop(3, rob, 10).

%cart(TicketNumber, Product, Price).
cart(1, eggs, 15).
cart(1, milk, 20).
cart(1, meat, 30).
cart(2, eggs, 10).
cart(2, soil, 5).
Run Code Online (Sandbox Code Playgroud)

totalCostShop应该返回总成本,例如shop(1,ash,12)的总成本应为65。

Use*_*213 5

从技术上讲,正确的答案是使用库(聚合)中的谓词。一个简单的查询:

?- aggregate_all(sum(Price), cart(1, _, Price), Total).
Total = 65.
Run Code Online (Sandbox Code Playgroud)

您看,到处都没有清单!好吧,谁知道,也许后面隐藏着清单aggregate_all,但是我们看不到它们,无神论者?

在常量内存中进行聚合而不创建数据结构的一种方法是使用全局状态。通常不建议这样做,原因有两个:

  1. 总的来说,全球状态难以维持。
  2. 特别是在Prolog中,用于操纵全局状态的代码编写起来很笨拙。

这是您遍历事实并产生副作用的方法:

?- forall(cart(1, _, Price), format("~w~n", [Price])).
15
20
30
true.
Run Code Online (Sandbox Code Playgroud)

第一个参数forall是生成器(在这种情况下,cart/31中第一个参数中具有的那些行)。第二个论点forall是副作用。现在,您只需要添加到全局变量,而不必打印到标准输出:

?- nb_setval(total, 0),
   forall(cart(1, _, Price),
   (   nb_getval(total, X0),
       X1 is X0 + Price,
       nb_setval(total, X1)
   )),
   nb_getval(total, Total).
Total = 65.
Run Code Online (Sandbox Code Playgroud)

(并且请参阅下面的评论。)

您可以使用其他机制来保持全局状态。最可移植的方法是将(插入assert)插入到具有一行的表中,然后在每个步骤中读取并删除该行的值(带有retract),添加到该值,然后assert返回结果。如果您需要为自己的(应用程序级)簿记保持真正的全局状态,那么我已经在关系数据库中看到了这种舞步。

在正常使用中,这是不必要的。但是,了解它是如何完成的很有用,这就是为什么我不愿意写所有这些内容的原因。我们不能有这个“你不能有这个”狗屎。

  • 如果单击Aggregate_all / 3文档附近的(:-)图标,您将获得[实现](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/aggregate.pl?show= src#aggregate_all / 3),您会发现在这种情况下,它正在使用类似于上面的特殊代码。 (6认同)