2008-03-07

Progress with the RuleCompiler

Finally! It's starting to feel like I'm getting somewhere with PyRete's RuleCompiler. Earlier this week I decided to change quite a lot about the allowed syntax in a rule.

I've always kind of thought I'd have to have support for expressing rules in many different ways. For instance, I've tried to support at least three different approaches to expressing:

|CLIPS>(defrule rule
| (foo (attribute ?attr&:(> ?attr 10)))
| (bar (attribute ?attr))
| =>)
This way, which used to be the preferred way, you would write the rule as if you were writing procedural code:
>>> @prodsys.rule
... def rule(engine, foo = Foo, bar = Bar):
... if foo.attribute == bar.attribute and \
... foo.attribute > 10:
... pass
Another way, which always felt a bit weird and unnatural to me, was to use a (logical) variable:
>>> @prodsys.rule
... def rule(engine, foo = Foo, bar = Bar):
... if foo.attribute == attr and \
... bar.attribute == attr and \
... attr > 10:
... pass
The problem with it, and the reason I don't like it, is that it's not all that clear what is going to happen. There's no way of telling that attr is a logical variable and that makes it look like it's three ordinary comparisons, but it's not. In any other context the code would raise an Exception because the local variable attr is unbound and that's probably how most Python programmers will read it.

I thought that the above would cause a lot of confusion which is why I implemented another way of expressing the same thing which looks a lot more like the pattern-matching syntax of CLIPS:
>>> @prodsys.rule
... def rule(engine, foo = Foo, bar = Bar):
... if foo(attribute == attr and attr > 10) and \
... bar(attribute == attr):
... pass
This is, of course, also slightly problematic but at least there's no interpretation of the code that completely shadows how it's being used in PyRete.

It wasn't until I decided, quite recently, that I'll only support the last type of syntax (pattern matching-style) that I started to get somewhere with the RuleCompiler. It also meant that I could get rid of the object bindings in the parameter list.

The above rule should be written as:
>>> @prodsys.rule
... def rule(engine):
... if Foo(attribute == attr and attr > 10) and \
... Bar(attribute == attr):
... pass
It's only too bad that it takes me so long to figure these things out.

3 kommentarer:

woolfel sa...

that's cuz it's not easy :)

plus, if it was easy, it wouldn't be as rewarding.

Johan Lindberg sa...

True!

It's just that I feel like a brute-force search implementation in O(n^2) land and my self-image is more of a best-first search implementation :-)

woolfel sa...

haha. that's a simple problem to solve. I think of myself as a O(n^10) algorithm, so if I managed to do it faster, it's a success.

though often times, I find that pure dumb luck and banging my head randomly shortens the search time to O(n^2).