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