## Min/max from working memory and using custom collectorsIn our ongoing series on JRules Patterns, Adam Smolnik suggested covering a pattern used to determine the minimum or maximum value in a class present in working memory. We then extend the pattern to show custom collectors and touch on some inference topics. The domain object is extremely simple: an Order class with a single field, amount: package test; public class Order { double amount; public Order(double amount) { this.amount = amount; } public double getAmount() { return amount; } } This class is simply imported into the BOM and verbalized. You can then write a rule that will be fired when the minimum order amount is modified: definitions set minOrder to an order ; if there is no order where the amount of this order is less than the amount of minOrder , then print "Min order:" + the amount of minOrder ; The corresponding rule to find the maximum order amount is: definitions set maxOrder to an order ; if there is no order where the amount of this order is more than the amount of maxOrder , then print "Max order:" + the amount of maxOrder ;
So far, so simple! One point to note is that the min/max rule will fire for You can test your rules by writing a function called ilrman(Object obj) with the body: for( int n=0; n < 100; n++ ) { Order order1 = new Order(n); insert(order1); } execute(); You launch configuration should be set to call ilrmain at startup. The function simply creates some test orders, adds each to working memory and then calls execute on the rule engine. You should see the output: Min order:0.0 Max order:99.0 If you need to perform more advanced statistics on the items in working memory you can 'collect' the instances into a collector and then implement your operations using Java methods. For example, here is a collector than computes the mean order amount. First we implement the IlrCollection interface and add our statistical methods. You could specialise the IlrDefaultCollector class but in this case we implement the IlrCollection interface directly as it allows us to update the total amount of all orders in the collections each time an order is added, yielding better performance for the getMean method. package test; import ilog After creating the OrderCollector Java class update your BOM entry to import it into the BOM. You can then create a technical rule as follows: when { orders: collect (new OrderCollector()) Order() where (size()>0 ); } then { ilog This rule is collecting all orders using the OrderCollector and then calling the getMean method if there are any orders. If you run the 3 rules together you should see: Min order:0.0 Max order:99.0 Mean order: 49.5 Now we can have some more fun! Let's create a rule that adds negative orders to working memory for as long as the mean order amount is greater than a threshold value: when { orders: collect (new OrderCollector()) Order() where (size()>0 && orders.mean > 5 ); } then { insert new Order( -orders.mean * orders.size() / 2.0 ); } Running all four rules together should print: Min order:0.0 Max order:99.0 Mean order: 49.5 Min order:-2475.0 Mean order: 24.504950495049506 Mean order: 12.132352941176471 Mean order: 6.007281553398058 Mean order: 2.9747596153846154 Here we can see the min/max rules initially be fired as before for the values [0..99] and then the mean is calculated as 49.5 by our rule that prints the mean. Then the first instance of the rule to lower the mean fires and we insert an order of -2475, which causes the min rule to fire as -2475 < 0. We keep inserting negative orders and each insert causes the mean to be updated and printed. The cascade of updates to the min/max and mean rely on JRules' inference capabilities and the underlying RetePlus algorithm. I suggest you play with the code to get a feel for some of the cool tricks your can play with inference. Download the projects for the article here. |