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

时间:2019-06-25 19:59:57

标签: prolog swi-prolog

我试图在不使用list的情况下对谓词的值求和。对值求和,最后得到这些值的总和。

%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).

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

1 个答案:

答案 0 :(得分:5)

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

?- aggregate_all(sum(Price), cart(1, _, Price), Total).
Total = 65.

您看到的,没有列表!好吧,谁知道,也许aggregate_all后面藏着一些名单,但是我们看不到它们,无神吗?

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

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

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

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

forall的第一个参数是生成器(在这种情况下,cart/3表中那些在第一个参数中带有1的行)。 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.

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

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

在正常使用中,这是不必要的。 但是,知道它是如何完成的很有用,这就是为什么我不愿意写所有这些。我们不能拥有这种“你不能拥有”的狗屎。