首页 > 解决方案 > 如何在不使用列表的情况下对谓词值求和?

问题描述

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

%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。

标签: prologswi-prolog

解决方案


对您的问题的技术正确答案是使用库中的谓词(聚合)。一个简单的查询:

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

你看,任何地方都没有清单!好吧,谁知道呢,也许背后隐藏着名单aggregate_all,但我们看不到它们,amirite?

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

  1. 一般来说,全局状态很难维护;
  2. 特别是在 Prolog 中,用于操作全局状态的代码编写起来很笨拙。

以下是迭代事实并获得副作用的方法:

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

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

?- 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.

(请参阅下面的评论。)

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

在正常使用中,这是不必要的。但是,了解它是如何完成的很有用,这就是我费心写下所有这些的原因。我们不能有这个“你不能有这个”的狗屎。


推荐阅读