Simulating accumulate will never be performant compared to implementing
correctly within Rete. Completing our support for first order logic to
make Drools turing complete will be prioritised very early on - we need
to do forall and accumulate. Accumulate is not that difficult to do - in
fact I recommend you "branch" Drools and look at the 'exists' node as
accumulate isn't far from that - the harder part will be hooking this in
with the parser and builder. Come onto IRC and we will give you pointers.
If you don't have time for that - as I suspect its 2 to 3 days work for
a Drools noobie developer. When I get some time I'll see if I can think
of a way to achieve accumulate support, even if it isn't performant -
imagine it will be using a combination of rules to count the assertions
and the event model to reduce counts on retraction. You will probably
have to use hash maps quite extensibly too.
Mark
Juergen wrote:
In the absence of an accumulate in current drools 3 version, I tried
to create a set of rules to have some sort of running sum (or any
other accumulation function), but it needs as much as 3 helper objects
per object qualified for accumulation.
Anybody any ideas on reducing number of helper objects and
retracts/asserts? via xor-groups or no-loops?
one helper (price == -3) especially prevents looping of accumulate if
sum is modified by de-accumulate if some other object no longer
qualifies. Is it possible to have some sort of "no-loop" groups, where
RHS changes in working memory will not only not trigger activation of
the current rule but all rules in a group?
Integration test listed below.
I misused Cheese as helper object, didn't want to create a new class:
type="sum" holds accumulated sum, type="limit reached" is used as flag
indicating sum boundary crossings, (type=s, price elem { -1, -2, -3})
state accumulation status of qualified object s (Strings in this
case); where s.length() will be accumulated
Is it possible to have some sort of "retraction hook/listener" on
asserted objects by letting them implement a special interface?
This implementation assumes that if an object does not longer qualify
(e.g. is retracted), it will nevertheless be available (via helper,
price == -1) for de-accumulation when the rules are fired again.
Juergen
----------------------------------------------------------
/*
public void testLogicalAssertionsRunningSum() throws Exception {
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader(
getClass().getResourceAsStream( "test_LogicalAssertionsRunningSum.drl"
) ) );
Package pkg = builder.getPackage();
RuleBase ruleBase = getRuleBase();
ruleBase.addPackage( pkg );
WorkingMemory workingMemory = ruleBase.newWorkingMemory();
//workingMemory.addEventListener(new
org.drools.event.DebugAgendaEventListener());
//workingMemory.addEventListener(new
org.drools.event.DebugWorkingMemoryEventListener());
List list;
Cheese c = new Cheese("sum", 0);
FactHandle h = workingMemory.assertObject(c);
FactHandle h1 = workingMemory.assertObject("1");
workingMemory.fireAllRules();
assertEquals(1, c.getPrice());
System.err.println("sum in code=" + c);
FactHandle h2 = workingMemory.assertObject("123");
workingMemory.fireAllRules();
assertEquals(4, c.getPrice());
System.err.println("sum in code=" + c);
FactHandle h3 = workingMemory.assertObject("12");
workingMemory.fireAllRules();
assertEquals(6, c.getPrice());
System.err.println("sum in code=" + c);
workingMemory.retractObject( h1 );
workingMemory.fireAllRules();
assertEquals(5, c.getPrice());
System.err.println("sum in code=" + c);
workingMemory.retractObject( h2 );
workingMemory.fireAllRules();
assertEquals(2, c.getPrice());
System.err.println("sum in code=" + c);
workingMemory.retractObject( h3 );
workingMemory.fireAllRules();
assertEquals(0, c.getPrice());
System.err.println("sum in code=" + c);
}
*/
package org.drools.test;
import java.lang.String;
import org.drools.Cheese;
rule "object qualifies for accumulation"
when
s : String()
eval(s != "sum" && s != "limit reached")
then
Cheese c = new Cheese(s, -1);
assert(c);
assertLogical(new Cheese(c.getType(), -2));
end
rule "accumulate"
when
sum : Cheese(type == "sum")
Cheese(s : type, price == -1)
Cheese(type == s, price == -2)
not Cheese(type == s, price == -3)
then
//apply accumulate function
sum.setPrice(sum.getPrice() + s.length());
modify(sum);
assert(new Cheese(s, -3));
System.err.println("+sum=" + sum);
end
rule "de-accumulate"
when
sum : Cheese(type == "sum")
c : Cheese(s : type, price == -1)
a : Cheese(type == s, price == -3)
not Cheese(type == s, price == -2)
then
//apply inverse function
sum.setPrice(sum.getPrice() - s.length());
modify(sum);
retract(c);
retract(a);
System.err.println("-sum=" + sum);
end
rule "do-something-when-limit-reached"
when
Cheese(type == "sum", price > 3)
not Cheese(type == "limit reached")
then
assert(new Cheese("limit reached", -1));
System.err.println("limit reached");
end
rule "do-something-when-limit-below"
when
Cheese(type == "sum", price <= 3)
c : Cheese(type == "limit reached")
then
retract(c);
System.err.println("limit below");
end