我如何在 Drools 中嵌套 foralls?
How do I do nested foralls in Drools?
问题
考虑这些类:
class BookCase { ArrayList<Book> books }
class Book { ArrayList<Page> pages }
class Page { String color }
考虑到这个自然语言规则:
When all pages in a bookcase are black, do A
最简单的方法是嵌套 forall 子句,但在 Drools 中不能这样做,因为 forall 子句只允许在内部包含模式(不是条件元素,forall 子句是什么)!
那我该如何在 Drools 中表达呢?
在我写问题的时候,我想我自己找到了答案:
BookCase($caseContents : books)
$bookWithOnlyBlackPages : ArrayList<Page>() from $caseContents
forall ( $page : Page(this memberOf $bookWithOnlyBlackPages)
Page(this == $page,
color == "black") )
forall ( $bookInCase : ArrayList<Page>(this memberOf $caseContents)
ArrayList<Page>(this == $bookInCase,
this == $bookWithOnlyBlackPages) )
这很接近,但不太正确:
rule "all black pages"
when
BookCase( $books: books )
$book: Book( $pages: pages ) from $books
not Page( color != "black" ) from $pages
then
System.out.println( "doing A" );
end
问题是,对于每本书,这将触发一次,其中所有页面都是黑色的。要评估所有书籍中的所有页面,可以 assemble 列出所有页面并确保它们都是黑色的:
rule "all black pages, take 2"
when
BookCase( $books: books )
$pages: ArrayList() from accumulate( Book( $ps: pages ) from $books,
init( ArrayList list = new ArrayList(); ),
action( list.addAll( $ps ); ),
result( list ) )
not Page( color != "black" ) from $pages
then
System.out.println( "doing A" );
end
如果不使用 forall(p1 p2 p3...)
,实际上可以嵌套多个 forall,但 equivalent not(p1 and not(and p2 p3...))
。然后,为了防止个别书籍和页面触发规则,在它们之间添加一个 exists
。
rule 'all pages in bookcase are black'
when
(and
$bookCase: BookCase()
(not (exists (and
$book: Book() from $bookCase.books
not( (and
not( (exists (and
$page: Page() from $book.pages
not( (and
// slightly different constraint than I used in question
eval($page.color == $book.color)
) )
) ) )
) )
) ) )
)
then
...
end
与使用 accumulate
创建所有页面的平面列表不同,这将保持 $page
的上下文,也就是说,当页面被置于其父书籍的约束中时,如上面的例子,在这个解决方案中,Drools 仍然 'knows' 父书是什么。
问题
考虑这些类:
class BookCase { ArrayList<Book> books }
class Book { ArrayList<Page> pages }
class Page { String color }
考虑到这个自然语言规则:
When all pages in a bookcase are black, do A
最简单的方法是嵌套 forall 子句,但在 Drools 中不能这样做,因为 forall 子句只允许在内部包含模式(不是条件元素,forall 子句是什么)!
那我该如何在 Drools 中表达呢?
在我写问题的时候,我想我自己找到了答案:
BookCase($caseContents : books)
$bookWithOnlyBlackPages : ArrayList<Page>() from $caseContents
forall ( $page : Page(this memberOf $bookWithOnlyBlackPages)
Page(this == $page,
color == "black") )
forall ( $bookInCase : ArrayList<Page>(this memberOf $caseContents)
ArrayList<Page>(this == $bookInCase,
this == $bookWithOnlyBlackPages) )
这很接近,但不太正确:
rule "all black pages"
when
BookCase( $books: books )
$book: Book( $pages: pages ) from $books
not Page( color != "black" ) from $pages
then
System.out.println( "doing A" );
end
问题是,对于每本书,这将触发一次,其中所有页面都是黑色的。要评估所有书籍中的所有页面,可以 assemble 列出所有页面并确保它们都是黑色的:
rule "all black pages, take 2"
when
BookCase( $books: books )
$pages: ArrayList() from accumulate( Book( $ps: pages ) from $books,
init( ArrayList list = new ArrayList(); ),
action( list.addAll( $ps ); ),
result( list ) )
not Page( color != "black" ) from $pages
then
System.out.println( "doing A" );
end
如果不使用 forall(p1 p2 p3...)
,实际上可以嵌套多个 forall,但 equivalent not(p1 and not(and p2 p3...))
。然后,为了防止个别书籍和页面触发规则,在它们之间添加一个 exists
。
rule 'all pages in bookcase are black'
when
(and
$bookCase: BookCase()
(not (exists (and
$book: Book() from $bookCase.books
not( (and
not( (exists (and
$page: Page() from $book.pages
not( (and
// slightly different constraint than I used in question
eval($page.color == $book.color)
) )
) ) )
) )
) ) )
)
then
...
end
与使用 accumulate
创建所有页面的平面列表不同,这将保持 $page
的上下文,也就是说,当页面被置于其父书籍的约束中时,如上面的例子,在这个解决方案中,Drools 仍然 'knows' 父书是什么。