问题描述
我在Java中有三个类:Product,Sale和ProductType。 用例是按类型列出的所有产品的销售额。
示例:
- A类产品的总销售额:500欧元
- B类产品的总销售额:500欧元
在我的数据源(Optaplanner解决方案)中,我有许多A型和B型产品。 我用累积功能在流口水中写了一个约束,该约束按类型返回正确的总数,但计数了两次(每个产品实例)。
这是流口水的约束:
rule "products sales"
when
$product : Product( )
$total : Integer() from
accumulate ( Product($sales: sales,productType == $product.productType),init( int total =0;),action(total+=($sales.getPrice());),result( new Integer (total)))
then
System.out.println(
" Product : "+ $product.getName() +
" \nProduct Type : " + $productType +
" \nTotal sales : " + $total +
" \nPenalty : " + $product.getSale().totalSellsOfProductPenalty($total)
);
scoreHolder.penalize(kcontext,$product.getSale().totalSellsOfProductPenalty($total));
end
结果:
Product : MyPRODUCT_A
Product Type : A
Total sales : 120
Penalty : -60
Product : MyPRODUCT_A
Product Type : A
Total sales : 120
Penalty : -60
Subject : MyPRODUCT_B
Product Type : B
Total sales : 1200
Penalty : -600
=>罚款:-720
想要的结果:
Product : MyPRODUCT_A
Product Type : A
Total sales : 120
Penalty : -60
Subject : MyPRODUCT_B
Product Type : B
Total sales : 1200
Penalty : -600
=>罚款:-660
我该如何解决?
解决方法
这里的问题不关乎您的规则-很好,您正确使用了“累加”功能(许多人都遇到了问题,包括我自己在内),并且可以按照您的要求去做。
问题在于您要传递到规则中的数据。基本上,规则的结构方式是每个产品一旦触发。因此,基本上,您所看到的是在工作内存中存在两个MyPRODUCT_A和一个MyPRODUCT_B的情况。该规则为每个项目触发一次,因此您将获得MyPRODUCT_A的两个输出和MyPRODUCT_B的一个输出。
有几种方法可以解决此问题,最简单的方法是在工作内存中放置一个“标志”,以表明您已经针对给定的产品触发了该规则。另一个解决方案可能是从工作内存中收回所有该产品。还有其他方法可以解决此问题,但是这些是我可以想到的最简单的方法。
(请注意,这里没有 循环的情况。您的规则不是循环的;它触发的次数超出了您的预期。no-loop
之类的解决方案不会好。
解决方案1:工作内存中的标志
这通常是最简单的解决方案。基本上,每次触发给定产品的规则时,都会向工作内存中添加一个标志。然后,下次触发该规则时,它将在触发该规则之前检查当前产品的标志是否存在。
在此示例中,我假设您要保持产品名称唯一,因此将产品名称插入工作内存中。您应该根据实际需求,用例和模型进行调整。
rule "products sales"
when
// Get the name of the current product ($name)
$product: Product( $name: name )
// Check that there is no flag for $name
not( String(this == $name) )
// Accumulate as in original
$total: Integer() from
accumulate ( Product($sales: sales,productType == $product.productType),init( int total =0;),action(total+=($sales.getPrice());),result( new Integer (total)))
then
System.out.println(
" Product : "+ $product.getName() +
" \nProduct Type : " + $productType +
" \nTotal sales : " + $total +
" \nPenalty : " + $product.getSale().totalSellsOfProductPenalty($total)
);
scoreHolder.penalize(kcontext,$product.getSale().totalSellsOfProductPenalty($total));
// Insert the flag
insert($name);
end
基本上,每个产品触发累积规则后,其名称都会输入到工作存储器中。如果产品名称在触发规则时处于工作记忆中,则不会触发。这意味着执行触发规则的每个产品都必须具有唯一的名称;重复项将被忽略。
insert
操作向工作内存中添加了一条新信息,但 not 不会重新触发先前运行的规则。因此,如果以前触发了一条规则,则该规则已经结束并完成。但是,随后的比赛现在将知道新事实。
有一个非常相似的操作,称为update
。此其他操作将撤销所有所有规则,并重新评估所有条件。一旦开始调用更新,就会开始遇到跨多个执行循环规则的潜在问题。您有99%的可能性不想在这里这样做。
此方法的缺点通常与大多数情况下相同,在大多数情况下,您会带有“标志”或其他种类的信号灯-您正在处理的项目数可能会失控。如果您一次要评估几个产品,那么开销会很低。但是想像一下,您有成千上万的产品同时在系统中运行-可能您的工作内存中可能有大量的String。这也可能导致应用程序和硬件出现资源问题。 (有一个关键点,您应该开始对字符串进行内部实习,这超出了此答案的范围。)
但是对于一个简单的解决方案,此方法“快速又脏”。
解决方案2:收回物品
仅当您不需要产品存储其他任何内容时,此解决方案才有效。也就是说,一旦MyPRODUCT_A触发了您的规则,您就不再需要任何 MyPRODUCT_A。在这种情况下,只要一触发规则,我们就可以从工作内存中删除所有MyPRODUCT_A。
在示例规则中,我将再次收集具有相同名称值的所有产品,但是您可以根据用例和模型根据需要进行更新。
rule "products sales"
when
// Get the name of the current product ($name)
$product: Product( $name: name )
// Accumulate as in original
$total: Integer() from
accumulate ( Product($sales: sales,result( new Integer (total)))
// Collect all of the products with the same name
$duplicates: List() from collect( Product( name == $name ) )
then
System.out.println(
" Product : "+ $product.getName() +
" \nProduct Type : " + $productType +
" \nTotal sales : " + $total +
" \nPenalty : " + $product.getSale().totalSellsOfProductPenalty($total)
);
scoreHolder.penalize(kcontext,$product.getSale().totalSellsOfProductPenalty($total));
// Remove all of the products with the same name
for (Product product : $duplicates) {
retract(product);
}
end
(请注意,retract
和delete
做同样的事情。他们目前正在推动采用“删除”作为“插入”的对立面,但我们的Drools老用户从后台当天的偏好以“撤回”。)
因此,基本上,您将看到MyPRODUCT_A的第一个实例符合规则,记录,然后它将从工作内存中删除MyPRODUCT_A(及其本身)的所有其他实例。然后MyPRODUCT_B将遵守规则,并执行相同的操作。最终,工作记忆中将没有产品。
很显然,如果您仍然需要对Product进行其他处理,那么这不是可行的解决方案,因为如果从内存中删除了Products,这些其他规则将无法执行。但是,如果您有一个非常专业的数据集只能执行此操作,则可以采用这种方法。
正如我所说,有许多解决此问题的方法。您的规则保持不变。每个产品的正确触发。如何解决仅针对部分产品的问题是您面临的问题,但是如何准确地解决问题取决于您和您的要求。