我正在尝试实施约束,以便如果具有特定名称的元素至少有一次未分配,则解决方案将受到惩罚。
我尝试使用
GroupBy()
但它不起作用:
Constraint atLeastOne(ConstraintFactory cf) {
return cf
.forEach(Element.class)
.groupBy(Element::getName, ConstraintCollectors.countDistinct())
.filter((elementName, count) -> elementName == "foo" && count < 1)
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("At least one occurency");
}
我的想法是,如果不存在名称为“foo”的元素,则过滤器不会返回任何内容,因此不会强制执行约束。
我也尝试过使用
ifNotExists()
:
Constraint atLeastOne(ConstraintFactory cf) {
return cf
.forEach(Element.class)
.ifNotExists(Element.class, Joiners.filtering((a, b) -> a.getName() == "foo" && b.getName() == "foo"))
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("At least one occurency");
}
问题是使用
ifNotExists()
也会惩罚所有带有a.getName() != "foo"
的元素
有什么想法让它发挥作用吗?
在约束流中,
groupby()
只有在至少有一个输入元组时才会产生结果。这意味着“至少有 X”约束必须作为两个单独的约束来实现:
由于您使用的是分组键,因此您需要一种方法来访问“没有元素满足约束”情况下的所有可能键。创建一个包含所有可能名称的
ElementName
集合,并将其作为 @ProblemFactCollectionProperty
放在您的 @PlanningSolution
上。然后约束看起来像这样:
Constraint atLeastOne(ConstraintFactory cf) {
return cf
.forEach(ElementName.class)
.filter(elementName -> elementName.getName().equals("foo"))
.ifNotExists(Element.class, Joiners.equal(ElementName::getName, Element::getName))
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("At least one occurency");
}