[rules-dev] feedback on accumulate docs

Edson Tirelli tirelli at post.com
Sat Aug 11 11:39:48 EDT 2007


   Thanks a lot for the feedback! :)
   I will try to improve the docs, but I'm still not sure how to explain the
"tuple" thing in the accumulate context. :( Maybe we need to explain it in a
previous overall rule section and then link from the accumulate docs,
digging deep on the details associated with accumulate.

   []s
   Edson

2007/8/11, Geoffrey De Smet <ge0ffrey.spam at gmail.com>:
>
> As promised :) (be carefull what you wish for)
>
> - "<init code>: this is a semantic block of code in the selected dialect
> that will be executed once for each tuple, before iterating over the
> source objects."
>
> What is a "tuple"?
> Do you mean "once for each rule" or "once for each pattern match"?
> (I presume the first, after reading further.)
>
> - "Source objects" are facts that follow the source pattern I presume?
> Or is there a difference?
>
> - The method javadocs in the AverageAccumulateFunction example are a bit
> too besides the point IMHO. I'd leave them out.
>
>
> Summary: nothing seriously wrong with that documentation, looks good :)
>
> With kind regards,
> Geoffrey De Smet
>
>
> Edson Tirelli wrote:
> >
> >     Geoffrey,
> >
> >     The "sum" function that is shipped with drools always uses double
> > internally, but all numeric accumulate functions must have return type
> > Number to avoid class cast exceptions... I must write that in the
> > docs... so, recommendation is to do:
> >
> >      Number( $total : intValue ) from accumulate(
> >         Hop($distance : distance ), // distance is an int
> >         sum($distance)
> >      );
> >
> >     Now, having said that, the decision to use double is because we
> > needed a one size fits all to ship with drools. On the other hand I
> > tried to make as simple as possible to plug new or replace built-in
> > functions.  So, if you want a sum function that uses only integers, you
> > can easily develop and plug it your own. Since I just wrote the docs on
> > how to do it, may I ask you please to read them and provide a feedback
> > if they are good, need more info, shall I write them in another way,
> etc?
> >     BTW, when I say it is really simple to create another sum function,
> > I mean, you can do it in about 20 minutes. Really!
> >
> >     Find attached the HTML doc page I'm talking about.
> >
> >     []s
> >     Edson
> >
> >
> > 2007/7/22, Geoffrey De Smet < ge0ffrey.spam at gmail.com
> > <mailto:ge0ffrey.spam at gmail.com>>:
> >
> >     Hi guys,
> >
> >     I finally got around to experimenting with the accumulate support in
> >     LocalSearchSolver. Thanks for implementing it :)
> >
> >     I use nothing but int's in my calculations,
> >     but I get a ClassCastExceptions, as sum() always returns a Double
> and
> >     setSoftConstraintsBroken(int) failes.
> >
> >     In a previous benchmarks I 've proven that summing integers in
> double's
> >     instead of int's hurts performance for 10% or more (which is rather
> >     big).
> >
> >     Is there a sumInteger() available (or do I need to write it)? Or can
> it
> >     be overloaded over sum() in a much cleaner way?
> >
> >
> >
> >     rule "ScoreCalculator"
> >        when
> >          $scoreFact : ScoreFact(); // singleton
> >          $total : Integer() from accumulate(
> >             Hop($distance : distance ), // distance is an int
> >             sum($distance)
> >          );
> >        then
> >          $scoreFact.setSoftConstraintsBroken($total);
> >     end
> >
> >
> >     svn is still here, till it's ready for drools:
> >     https://taseree.svn.sourceforge.net/svnroot/taseree/trunk
> >
> >     --
> >     With kind regards,
> >     Geoffrey De Smet
> >
> >     _______________________________________________
> >     rules-dev mailing list
> >     rules-dev at lists.jboss.org <mailto:rules-dev at lists.jboss.org>
> >     https://lists.jboss.org/mailman/listinfo/rules-dev
> >
> >
> >
> >
> > --
> >   Edson Tirelli
> >   Software Engineer - JBoss Rules Core Developer
> >   Office: +55 11 3529-6000
> >   Mobile: +55 11 9287-5646
> >   JBoss, a division of Red Hat @ www.jboss.com <http://www.jboss.com>
> > ------------------------------------------------------------------------
> >
> > 3.6. Advanced Conditional Elements
> > Prev <ch03s05.html>   Chapter 3. The Rule Language     Next <
> ch03s07.html>
> >
> > ------------------------------------------------------------------------
> >
> >
> >     3.6. Advanced Conditional Elements
> >
> >
> >       Note
> >
> > /|(updated to Drools 4.0)|/
> >
> > Drools 4.0 introduces a whole new set of conditional elements in order
> > to support full First Order Logic expressiveness, as well as some
> > facilities for handling collections of facts. This section will detail
> > the following new Conditional Elements:
> >
> >     *
> >
> >       from
> >
> >     *
> >
> >       collect
> >
> >     *
> >
> >       accumulate
> >
> >     *
> >
> >       forall
> >
> >
> >       3.6.1. From
> >
> > The *from* Conditional Element allows users to specify a source for
> > patterns to reason over. This allows the engine to reason over data not
> > in the Working Memory. This could be a sub-field on a bound variable or
> > the results of a method call. It is a powerful construction that allows
> > out of the box integration with other application components and
> > frameworks. One common example is the integration with data retrieved
> > on-demand from databases using hibernate named queries.
> >
> > The expression used to define the object source is any expression that
> > follows regular MVEL syntax. I.e., it allows you to easily use object
> > property navigation, execute method calls and access maps and
> > collections elements.
> >
> > Here is a simple example of reasoning and binding on another pattern
> > sub-field:
> >
> > rule "validate zipcode"
> > when
> >     Person( $personAddress : address )
> >     Address( zipcode == "23920W") from $personAddress
> > then
> >     # zip code is ok
> > end
> >
> > With all the flexibility from the new expressiveness in the Drools
> > engine you can slice and dice this problem many ways. This is the same
> > but shows how you can use a graph notation with the 'from':
> >
> > rule "validate zipcode"
> > when
> >     $p : Person( )
> >     $a : Address( zipcode == "23920W") from $p.address
> > then
> >     # zip code is ok
> > end
> >
> > Previous examples were reasoning over a single pattern. The *from* CE
> > also support object sources that return a collection of objects. In that
> > case, *from* will iterate over all objects in the collection and try to
> > match each of them individually. For instance, if we want a rule that
> > applies 10% discount to each item in an order, we could do:
> >
> > rule "apply 10% discount to all items over US$ 100,00 in an order"
> > when
> >     $order : Order()
> >     $item  : OrderItem( value > 100 ) from $order.items
> > then
> >     # apply discount to $item
> > end
> >
> > The above example will cause the rule to fire once for each item whose
> > value is greater than 100 for each given order.
> >
> > The next example shows how we can reason over the results of a hibernate
> > query. The Restaurant pattern will reason over and bind with each result
> > in turn:
> >
> >
> >       3.6.2. Collect
> >
> > The *collect* Conditional Element allows rules to reason over collection
> > of objects collected from the given source or from the working memory. A
> > simple example:
> >
> > import java.util.ArrayList
> >
> > rule "Raise priority if system has more than 3 pending alarms"
> > when
> >     $system : System()
> >     $alarms : ArrayList( size >= 3 )
> >               from collect( Alarm( system == $system, status ==
> 'pending' ) )
> > then
> >     # Raise priority, because system $system has
> >     # 3 or more alarms pending. The pending alarms
> >     # are $alarms.
> > end
> >
> > In the above example, the rule will look for all pending alarms in the
> > working memory for each given system and group them in ArrayLists. If 3
> > or more alarms are found for a given system, the rule will fire.
> >
> > The *collect* CE result pattern can be any concrete class that
> > implements tha java.util.Collection interface and provides a default
> > no-arg public constructor. I.e., you can use default java collections
> > like ArrayList, LinkedList, HashSet, etc, or your own class, as long as
> > it implements the java.util.Collection interface and provide a default
> > no-arg public constructor.
> >
> > Both source and result patterns can be constrained as any other pattern.
> >
> > Variables bound before the *collect* CE are in the scope of both source
> > and result patterns and as so, you can use them to constrain both your
> > source and result patterns. Although, the /collect( ... )/ is a scope
> > delimiter for bindings, meaning that any binding made inside of it, is
> > not available for use outside of it.
> >
> > Collect accepts nested *from* elements, so the following example is a
> > valid use of *collect*:
> >
> > import java.util.LinkedList;
> >
> > rule "Send a message to all mothers"
> > when
> >     $town : Town( name == 'Paris' )
> >     $mothers : LinkedList()
> >                from collect( Person( gender == 'F', children > 0 )
> >                              from $town.getPeople()
> >                            )
> > then
> >     # send a message to all mothers
> > end
> >
> >
> >       3.6.3. Accumulate
> >
> > The *accumulate* Conditional Element is a more flexible and powerful
> > form of *collect* Conditional Element, in the sense that it can be used
> > to do what *collect* CE does and also do things that *collect* CE is not
> > capable to do. Basically what it does is it allows a rule to iterate
> > over a collection of objects, executing custom actions for each of the
> > elements, and at the end return a result object.
> >
> > The general syntax of the *accumulate* CE is:
> >
> > /|<result pattern>|/ from accumulate( /|<source pattern>|/,
> >                                   init( /|<init code>|/ ),
> >                                   action( /|<action code>|/ ),
> >                                   reverse( /|<reverse code>|/ ),
> >                                   result( /|<result expression>|/ ) )
> >
> > The meaning of each of the elements is the following:
> >
> >     *
> >
> >       *<source pattern>*: the source pattern is a regular pattern that
> >       the engine will try to match against each of the source objects.
> >
> >     *
> >
> >       *<init code>*: this is a semantic block of code in the selected
> >       dialect that will be executed once for each tuple, before
> >       iterating over the source objects.
> >
> >     *
> >
> >       *<action code>*: this is a semantic block of code in the selected
> >       dialect that will be executed for each of the source objects.
> >
> >     *
> >
> >       *<reverse code>*: this is an optional semantic block of code in
> >       the selected dialect that if present will be executed for each
> >       source object that no longer matches the source pattern. The
> >       objective of this code block is to "undo" any calculation done in
> >       the <action code> block, so that the engine can do decremental
> >       calculation when a source object is modified or retracted, hugely
> >       improving performance of these operations.
> >
> >     *
> >
> >       *<result expression>*: this is a semantic expression in the
> >       selected dialect that is executed after all source objects are
> >       iterated.
> >
> >     *
> >
> >       *<result pattern>*: this is a regular pattern that the engine
> >       tries to match against the object returned from the <result
> >       expression>. If it matches, the *accumulate* conditional element
> >       evaluates to *true* and the engine proceeds with the evaluation of
> >       the next CE in the rule. If it does not matches, the *accumulate*
> >       CE evaluates to *false* and the engine stops evaluating CEs for
> >       that rule.
> >
> > It is easier to understand if we look at an example:
> >
> > rule "Apply 10% discount to orders over US$ 100,00"
> > when
> >     $order : Order()
> >     $total : Number( doubleValue > 100 )
> >              from accumulate( OrderItem( order == $order, $value : value
> ),
> >                               init( double total = 0; ),
> >                               action( total += $value; ),
> >                               reverse( total -= $value; ),
> >                               result( total ) )
> > then
> >     # apply discount to $order
> > end
> >
> > In the above example, for each Order() in the working memory, the engine
> > will execute the *init code* initializing the total variable to zero.
> > Then it will iterate over all OrderItem() objects for that order,
> > executing the *action* for each one (in the example, it will sum the
> > value of all items into the total variable). After iterating over all
> > OrderItem, it will return the value corresponding to the *result
> > expression* (in the above example, the value of the total variable).
> > Finally, the engine will try to match the result with the Number()
> > pattern and if the double value is greater than 100, the rule will fire.
> >
> > The example used java as the semantic dialect, and as such, note that
> > the usage of ';' is mandatory in the init, action and reverse code
> > blocks. The result is an expression and as such, it does not admit ';'.
> > If the user uses any other dialect, he must comply to that dialect
> > specific syntax.
> >
> > As mentioned before, the *reverse code* is optional, but it is strongly
> > recommended that the user writes it in order to benefit from the
> > /improved performance on update and retracts/.
> >
> > The *accumulate* CE can be used to execute any action on source objects.
> > The following example instantiates and populates a custom object:
> >
> > rule "Accumulate using custom objects"
> > when
> >     $person   : Person( $likes : likes )
> >     $cheesery : Cheesery( totalAmount > 100 )
> >                 from accumulate( $cheese : Cheese( type == $likes ),
> >                                  init( Cheesery cheesery = new
> Cheesery(); ),
> >                                  action( cheesery.addCheese( $cheese );
> ),
> >                                  reverse( cheesery.removeCheese( $cheese
> ); ),
> >                                  result( cheesery ) );
> > then
> >     // do something
> > end
> >
> >
> >         3.6.3.1. Accumulate Functions
> >
> > The accumulate CE is a very powerful CE, but it gets real declarative
> > and easy to use when using predefined functions that are known as
> > Accumulate Functions. They work exactly like accumulate, but instead of
> > explicitly writing custom code in every accumulate CE, the user can use
> > predefined code for common operations.
> >
> > For instance, the rule to apply discount on orders written in the
> > previous section, could be written in the following way, using
> > Accumulate Functions:
> >
> > rule "Apply 10% discount to orders over US$ 100,00"
> > when
> >     $order : Order()
> >     $total : Number( doubleValue > 100 )
> >              from accumulate( OrderItem( order == $order, $value : value
> ),
> >                               sum( $value ) )
> > then
> >     # apply discount to $order
> > end
> >
> > In the above example, sum is an AccumulateFunction and will sum the
> > $value of all OrderItems and return the result.
> >
> > Drools 4.0 ships with the following built in accumulate functions:
> >
> >     *
> >
> >       average
> >
> >     *
> >
> >       min
> >
> >     *
> >
> >       max
> >
> >     *
> >
> >       count
> >
> >     *
> >
> >       sum
> >
> > These common functions accept any expression as input. For instance, if
> > someone wants to calculate the average profit on all items of an order,
> > a rule could be written using the average function:
> >
> > rule "Average profit"
> > when
> >     $order : Order()
> >     $profit : Number()
> >               from accumulate( OrderItem( order == $order, $cost : cost,
> $price : price )
> >                                average( 1 - $cost / $price ) )
> > then
> >     # average profit for $order is $profit
> > end
> >
> > Accumulate Functions are all pluggable. That means that if needed,
> > custom, domain specific functions can easily be added to the engine and
> > rules can start to use them without any restrictions. To implement a new
> > Accumulate Functions all one needs to do is to create a java class that
> > implements the org.drools.base.acumulators.AccumulateFunction interface
> > and add a line to the configuration file or set a system property to let
> > the engine know about the new function. As an example of an Accumulate
> > Function implementation, the following is the implementation of the
> > "average" function:
> >
> > /*
> >  * Copyright 2007 JBoss Inc
> >  *
> >  * Licensed under the Apache License, Version 2.0 (the "License");
> >  * you may not use this file except in compliance with the License.
> >  * You may obtain a copy of the License at
> >  *
> >  *      http://www.apache.org/licenses/LICENSE-2.0
> >  *
> >  * Unless required by applicable law or agreed to in writing, software
> >  * distributed under the License is distributed on an "AS IS" BASIS,
> >  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> >  * See the License for the specific language governing permissions and
> >  * limitations under the License.
> >  *
> >  * Created on Jun 21, 2007
> >  */
> > package org.drools.base.accumulators;
> >
> >
> > /**
> >  * An implementation of an accumulator capable of calculating average
> values
> >  *
> >  * @author etirelli
> >  *
> >  */
> > public class AverageAccumulateFunction implements AccumulateFunction {
> >
> >     protected static class AverageData {
> >         public int    count = 0;
> >         public double total = 0;
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see
> org.drools.base.accumulators.AccumulateFunction#createContext()
> >      */
> >     public Object createContext() {
> >         return new AverageData();
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see org.drools.base.accumulators.AccumulateFunction#init(
> java.lang.Object)
> >      */
> >     public void init(Object context) throws Exception {
> >         AverageData data = (AverageData) context;
> >         data.count = 0;
> >         data.total = 0;
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see org.drools.base.accumulators.AccumulateFunction#accumulate(
> java.lang.Object, java.lang.Object)
> >      */
> >     public void accumulate(Object context,
> >                            Object value) {
> >         AverageData data = (AverageData) context;
> >         data.count++;
> >         data.total += ((Number) value).doubleValue();
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see org.drools.base.accumulators.AccumulateFunction#reverse(
> java.lang.Object, java.lang.Object)
> >      */
> >     public void reverse(Object context,
> >                         Object value) throws Exception {
> >         AverageData data = (AverageData) context;
> >         data.count--;
> >         data.total -= ((Number) value).doubleValue();
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see org.drools.base.accumulators.AccumulateFunction#getResult(
> java.lang.Object)
> >      */
> >     public Object getResult(Object context) throws Exception {
> >         AverageData data = (AverageData) context;
> >         return new Double( data.count == 0 ? 0 : data.total / data.count);
> >     }
> >
> >     /* (non-Javadoc)
> >      * @see
> org.drools.base.accumulators.AccumulateFunction#supportsReverse()
> >      */
> >     public boolean supportsReverse() {
> >         return true;
> >     }
> >
> > }
> >
> > The code for the function is very simple, as we could expect, as all the
> > "dirty" integration work is done by the engine. Finally, to plug the
> > function into the engine, we added it to the configuration file:
> >
> > drools.accumulate.function.average =
> org.drools.base.accumulators.AverageAccumulateFunction
> >
> > Where "drools.accumulate.function." is a prefix that must always be
> > used, "average" is how the function will be used in the rule file, and
> > "org.drools.base.accumulators.AverageAccumulateFunction" is the fully
> > qualified name of the class that implements the function behavior.
> >
> >
> >       3.6.4. Forall
> >
> > *Forall* is the Conditional Element that completes the First Order Logic
> > support in Drools. The syntax is very simple:
> >
> > forall( /|<select pattern>|/ /|<constraint patterns>|/ )
> >
> > The *forall* Conditional Element will evaluate to true when all facts
> > that match the /|<select pattern>|/ match all the /|<constraint
> > patterns>|/. Example:
> >
> > rule "All english buses are red"
> > when
> >     forall( $bus : Bus( type == 'english')
> >                    Bus( this == $bus, color = 'red' ) )
> > then
> >     # all english buses are red
> > end
> >
> > In the above rule, we "select" all Bus object whose type is "english".
> > Then, for each fact that matchs this pattern we evaluate the following
> > patterns and if they match, the forall CE will evaluate to true. Another
> > example:
> >
> > rule "all employees have health and dental care programs"
> > when
> >     forall( $emp : Employee()
> >             HealthCare( employee == $emp )
> >             DentalCare( employee == $emp )
> >           )
> > then
> >     # all employees have health and dental care
> > end
> >
> > Forall can be nested inside other CEs for complete expressiveness. For
> > instance, *forall* can be used inside a *not* CE:
> >
> > rule "not all employees have health and dental care"
> > when
> >     not forall( $emp : Employee()
> >                 HealthCare( employee == $emp )
> >                 DentalCare( employee == $emp )
> >               )
> > then
> >     # not all employees have health and dental care
> > end
> >
> > As a side note, forall Conditional Element is equivalent to writing:
> >
> > not( /|<select pattern>|/ and not ( and /|<constraint patterns>|/ ) )
> >
> > Also, it is important to note that *forall is a scope delimiter*, so it
> > can use any previously bound variable, but no variable bound inside it
> > will be available to use outside of it.
> >
> > ------------------------------------------------------------------------
> > Prev <ch03s05.html>   Up <ch03.html>   Next <ch03s07.html>
> > 3.5. Rule     Home <title.html> | ToC <bk01-toc.html>  3.7. Query
> >
> >
> > ------------------------------------------------------------------------
> >
> > _______________________________________________
> > rules-dev mailing list
> > rules-dev at lists.jboss.org
> > https://lists.jboss.org/mailman/listinfo/rules-dev
>
> _______________________________________________
> rules-dev mailing list
> rules-dev at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-dev
>



-- 
  Edson Tirelli
  Software Engineer - JBoss Rules Core Developer
  Office: +55 11 3529-6000
  Mobile: +55 11 9287-5646
  JBoss, a division of Red Hat @ www.jboss.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/rules-dev/attachments/20070811/dceed725/attachment.html 


More information about the rules-dev mailing list