|
|
|
|
||||||||||||||
|
|
OPSJ
OPSJ is designed to let you add rules to Java. OPSJ is not a new language, but rather a set of compatible extensions to standard Java. With OPSJ, you write your entire program in Java, using OPSJ's rule-based extensions where appropriate. This new approach provides a number of important advantages:
Implementation DetailsThe OPSJ implementation was designed to be portable and efficient:
In OPSJ, rules are grouped into units called "Knowledge Sources." An OPSJ program may contain one knowledge source, or many. OPSJ Knowledge Sources are thread-safe. So, for example, if you had a Knowledge Source running in one thread, and a user interface in another thread, you could add incoming data to the Knowledge Source at any time without worry. Development EnvironmentThe OPSJ system has a windowed, point and click development environment that allows you to trace execution of the rules, set breakpoints, examine the state of the system, etc. The development environment can be used with both stand-alone rule bases and rule bases that are embedded as subsystems in larger programs--even if the larger program has its own graphical interface. The development environment can handle programs with multiple Knowledge Sources; each Knowledge Source gets its own window.
OPSJ supports interactive development of rules. You can be in the middle of a run, and decide to make a change to the rules. OPSJ will load the new rules, bring them up to date, and permit you to continue the run from where you were. You never have to kill a program to make a change unless you want to. Example of an OPSJ RuleOPSJ conforms very closely to standard Java syntax, which makes it easier for Java programmers to learn and use. For an example of an OPSJ rule, suppose you were writing a system to monitor the operation of a chemical plant, and you needed to implement the following rule:
IF a new temperature reading arrives with a reading that is above the Yellow level, but below the Red level, THEN write out a warning message and invoke the warning routine. Further, suppose temperature readings are put into objects of the following class:
class TempReading {
real getReading() { ... };
String getName() { ... };
boolean getNewFlag() { ... };
public final static real YellowLimit = ... ; public final static real RedLimit = ... ; ... } An OPSJ rule to implement the above English rule would be:
rule(WARNINGPRIORITY) HandleYellowLimit if {
// there is a new TempReading with reading in the // yellow zone, bind variable tnew to that object tnew: TempReading( tnew.getNewFlag() == true, tnew.getReading() >= tnew.YellowLimit, tnew.getReading() < tnew.RedLimit); } do {
System.err.println("Yellow temp limit exceeded on tank "
+ tnew.getTankName()); System.err.println("Reading is: " + tnew.getReading());
HandleYellow(tnew); } The condition part of the rule is the block between the "if" and the "do." The action part is the block after the "do." Notice that the action part of the rule is standard Java code. This rule contains only a few method calls in its action part, but OPSJ allows any legal Java code to be used in the action part. Although it is not appropriate to go into the syntax of OPSJ conditions here, the interpretation of this condition part should be fairly clear to any Java. The constant WARNINGPRIORITY is a programmer-defined priority level. It is used to insure that this error-handling rule can take precedence over the rules for handling normal conditions. Expressive PowerOPSJ is an extremely powerful language. Among its more important features are: · It can call arbitrary Java methods from the condition parts of rules. · It supports both forward and backward chaining. · It permits the use of multiple else parts in each rule.
As a result of features like these, OPSJ systems can often solve problems using significantly fewer rules than less powerful systems require. This results in faster development, more understandable systems, and reduced maintenance costs. For an example of this, consider the following four rules. These are a direct translation into OPSJ of four rules taken from a standard CLIPS example system. These rules are from a version of the toy “monkey and bananas” program. This group of rules is responsible for the planning and execution of the actions required for the monkey to move an object to a new location. The third rule is in a sense the “main” rule of this group; it fires when all preconditions are satisfied and changes the state of the system to indicate the object is in the new location. The first two rules check preconditions that the third rule requires, and generate new Goal objects when a precondition is not met. These Goal objects cause other rules to attempt to satisfy the preconditions. The fourth rule is essentially an error-handler; it fires if an object does not really need to be moved (because is already in the desired location).
rule holdObjectToMove if { task: Goal ( task.name == "move", var obj = task.arg1, var place = task.arg2); t: Thing ( t == obj, t.location != place, t.weight == "light"); mky: Monkey ( mky.holding != obj); } do { insert(new Goal("hold", obj)); }
rule moveObjectToPlace if { task: Goal ( task.name == "move", var obj = task.arg1, var place = task.arg2); t: Thing ( t == obj, t.location != place, t.weight == "light"); mky: Monkey ( mky.holding == obj, mky.location != place); } do { insert(new Goal("walk-to", place)); }
rule dropObjectOnceMoved if { task: Goal ( task.name == "move", var obj = task.arg1, var place = task.arg2); t: Thing ( t == obj, t.location != place, t.weight == "light"); mky: Monkey ( mky.holding == obj, mky.location == place); } do { System.out.println("Monkey drops the " + t.name); modify(t); t.onTopOf = FLOOR; modify(mky); mky.holding = null; delete(task); }
rule alreadyMovedObject if { task: Goal ( task.name == "move", var obj = task.arg1, var place = task.arg2); t: Thing ( t == obj, t.location == place); } do { delete(task); }
As you see, these rules have similar but not identical condition parts. This creates at least two very significant problems: · To understand the interactions among these rules you have to read each rule closely and determine just where its condition differs from the other conditions. · If you have to make a change in the conditions, you have to examine each rule to determine whether that change applies to that rule.
Using OPSJ’s more powerful rule language, you can combine the above four rules into a single rule, which is easier to write, and much easier to understand and maintain. (This rule uses two new OPSJ features: The “4” in brackets labels a test in the condition part; the else part of this rule is executed with the labeled condition fails. The “need” keyword is used to indicate to OPSJ that it should insure the following condition is satisfied, by creating a new subtask if necessary.)
rule carryLightObject if { task: ChangeValue ( task.getClassName() == “Thing”, task.getField() == “onTopOf”, task.getNewValue() == FLOOR); t: Thing ( t == task.getObject()); mky: Monkey; [4] (t.location != task.getNewValue()); (t.weight == "light"); need (mky.holding == task.getObject()); need (mky.location == task.getNewValue()); } do { delete(task); System.out.println("Monkey places " + t.name + " on floor at " + mky.location); modify(mky); mky.holding = null; modify(t); t.onTopOf = FLOOR; } else (4) { delete(task); } Maintaining rules like this one is far easier than maintaining rules like the four above.
|
|||||||||||||||
|
|