- /*
- manners示例是一个安排座位的例子,它的要求如下:
- 1、客人应当按照男女性别相邻而坐,围成一圈
- 2、假设客人会有2-3个个人爱好(爱好的可选范围是3种),为了让大家能够更好的交流,
- 要安排每个人都和相邻座位的两个人有共同的爱好,并且与左右两边的共同爱好不要相同
- 3、客人的人数在本例中为64人,男女各一半
- 解题思路:
- 1) 按照规则引擎的特性,首先应当将所有可能的数据准备好,从本例来看,客人是主要的数据对象,
- 姓名/性别/兴趣 是三个主要属性,姓名和性别是固定的,可能有多个选择的就是兴趣爱好,
- 那么首先应当将客人的兴趣分别与姓名/性别组合后放入WorkingMemory
- 2) 再考虑一下,本例中规定了每个客人都会有三种爱好里的两种,也就是说任意两个人都必然有相同爱好
- 只要注意不要性别相同以及两边爱好一致就可以;那么只要先安排一个人坐下(这个人应当三种爱好都有),
- 然后找一个性别不同,有一种相同爱好的客人A坐在左边;接下去找一个坐在A旁边的客人B,同样与A性别
- 不同,并且和前一位的共同爱好也不同。这样一直排下去,直到最后一个座位。这里解释一下为什么一开始
- 第一个人要三种爱好齐备,因为这种算法有一个漏洞是没有考虑第一个客人和最后一个客人是相邻而坐的,
- 如果第一个客人和最后一个客人只有一个共同兴趣而这个兴趣又正好是第一和第二个客人的共同兴趣,那么
- 这种算法会得到不正确的结果,但只要第一个坐下来的人三种爱好都有,那么必然和两边的人都有不同爱好,
- 这也弥补了算法的不同。
- 注:从本例中准备的数据和算法来看并没有考虑到这个问题,虽然按照原始数据可以得到正确的结果,但是
- 进行针对性的调整后就会发现结果会有问题
- 3) 因为座位是一个一个找出来的,因此要有合适的流程控制方法。
- */
- package org.drools.benchmark.manners
- # 从客人中找出一位放入第一个座位
- # 根据Drools引擎的特性,应当是最后一个加入的Guest
- # 利用Context.state属性控制该规则只执行一次
- rule assignFirstSeat
- when
- # Context用于流程控制
- context : Context( state == Context.START_UP )
- #
- # 原例中使用的是 Guest() , 获得最后插入的Fact,但是这样会引起之前提到的Bug
- # 因此在下面进行了改进,保证找出三个兴趣都有的客人
- #
- guest : Guest( $name:name , $hobby : hobby )
- Guest ( name == $name , hobby != $hobby , $hobby1 : hobby )
- Guest ( name == $name , hobby != $hobby , hobby != $hobby1 )
- count : Count()
- then
- String guestName = guest.getName();
- # Seating用于记录每个座位上的客人,是寻找下一个座位的客人的依据
- Seating seating = new Seating( count.getValue(), 1, true, 1, guestName, 1, guestName);
- insert( seating );
- # Path用来避免已经入座的客人再次被选中
- Path path = new Path( count.getValue(), 1, guestName );
- insert( path );
- # 用于计数,并作为Seating的ID
- count.setValue( count.getValue() + 1 );
- update( count );
- System.out.println( "assign first seat : " + seating + " : " + path );
- # 进入找下一个座位环节
- context.setState( Context.ASSIGN_SEATS );
- update( context );
- end
- # 寻找相邻的同伴
- rule findSeating
- when
- context : Context( state == Context.ASSIGN_SEATS )
- # 获得最近安排好座位的客人
- $s:Seating( seatingId:id, seatingPid:pid, pathDone == true, seatingRightSeat:rightSeat, seatingRightGuestName:rightGuestName )
- # 找到一位有共同兴趣但性别不同的相邻客人
- Guest( name == seatingRightGuestName, rightGuestSex:sex, rightGuestHobby:hobby )
- Guest( leftGuestName:name , sex != rightGuestSex, hobby == rightGuestHobby )
- count : Count()
- # 排除已经入座的客人
- #
- # 原例中使用了Path.id作为一个约束条件,而事实上要排除已经入座的客人只要判断guestName就可以了
- # 因此原例中的makePath规则如果注释掉,然后将下面的语句改为
- # not ( Path( guestName == leftGuestName) )
- # 执行后也可以得到完全相同的结果
- # Manners是作为规则引擎的测试基准发布的,因此这里完全按照标准实现,所以这里虽然
- # 可以进行改进,但是可能因为为了测试规则引擎,因此保留这种算法
- #
- not ( Path( id == seatingId, guestName == leftGuestName) )
- # 排除入座客人已经和使用过的兴趣,保证两边相邻的客人共同兴趣不一样
- not ( Chosen( id == seatingId, guestName == leftGuestName, hobby == rightGuestHobby) )
- then
- int rightSeat = seatingRightSeat;
- int seatId = seatingId;
- int countValue = count.getValue();
- # 安排一个新的座位
- Seating seating = new Seating( countValue, seatId, false, rightSeat, seatingRightGuestName, rightSeat + 1, leftGuestName );
- insert( seating );
- # 记录已经入座的客人
- Path path = new Path( countValue, rightSeat + 1, leftGuestName );
- insert( path );
- # 记录已经使用的兴趣爱好
- Chosen chosen = new Chosen( seatId, leftGuestName, rightGuestHobby );
- insert( chosen );
- System.err.println( "find seating : " + seating + " : " + path + " : " + chosen);
- count.setValue( countValue + 1 );
- update( count );
- context.setState( Context.MAKE_PATH );
- update( context );
- retract ( $s );
- end
- /*
- 这个规则的执行将为之前所有入座的客人建立一个路径
- 如果使用一个打印语句,部分打印内容如下:
- makePath :[Path id=1, seat=1, guest=63]
- makePath :[Path id=2, seat=1, guest=63]
- makePath :[Path id=2, seat=2, guest=52]
- makePath :[Path id=3, seat=2, guest=52]
- makePath :[Path id=3, seat=1, guest=63]
- makePath :[Path id=3, seat=3, guest=64]
- makePath :[Path id=4, seat=3, guest=64]
- makePath :[Path id=4, seat=1, guest=63]
- makePath :[Path id=4, seat=2, guest=52]
- makePath :[Path id=4, seat=4, guest=50]
- makePath :[Path id=5, seat=4, guest=50]
- makePath :[Path id=5, seat=2, guest=52]
- makePath :[Path id=5, seat=1, guest=63]
- makePath :[Path id=5, seat=3, guest=64]
- makePath :[Path id=5, seat=5, guest=62]
- ......
- 也就是每个id的数值就对应有多少个Path,而事实上这种数据模型对本例来说并不是太需要
- 在之前的findSeating规则中有说明。
- */
- rule makePath
- when
- Context( state == Context.MAKE_PATH )
- Seating( seatingId:id, seatingPid:pid, pathDone == false )
- $path : Path( id == seatingPid, pathGuestName:guestName, pathSeat:seat )
- not Path( id == seatingId, guestName == pathGuestName )
- then
- System.out.println ( "makePath :" + $path );
- insert( new Path( seatingId, pathSeat, pathGuestName ) );
- end
- # 结束座位的路径设置
- rule pathDone
- when
- context : Context( state == Context.MAKE_PATH )
- seating : Seating( pathDone == false )
- then
- seating.setPathDone( true );
- update( seating );
- context.setState( Context.CHECK_DONE );
- update( context );
- end
- # 检查是否安排的客人座位已经到达指定人数,结束规则运算
- rule areWeDone
- when
- context : Context( state == Context.CHECK_DONE )
- LastSeat( lastSeat: seat )
- Seating( rightSeat == lastSeat )
- then
- context.setState(Context.PRINT_RESULTS );
- update( context );
- end
- # 如果之前的areWeDone规则没有允许,则继续座位查找
- rule continue
- when
- context : Context( state == Context.CHECK_DONE )
- then
- context.setState( Context.ASSIGN_SEATS );
- update( context );
- end
- # 打印完成提示
- rule allDone
- when
- context : Context( state == Context.PRINT_RESULTS )
- then
- System.out.println( "All Done" );
- end
- //query getResults
- // context : Context( state == Context.PRINT_RESULTS )
- //end
Drools4:Manners示例分析
最新推荐文章于 2024-10-15 19:28:18 发布