Filed under: Uncategorised
Comments: None
Rules are invoked on instances of classes. They can compute the values of one or more attributes and/or custom variables using non-recursive backward chaining, i.e. an attribute or a variable cannot depend on itself. This is much more powerful than just a run-time editable switch or case statement. It remains an important element of the AI development toolkit, alongside newer big data and neural network techniques. It can solve for multiple unknowns and introduce new goals while being evaluated. In our example, we look at a simple case from Wikipedia’s discussion of backward chaining rule sets. I encourage you to read this Wikipedia article (and related ones) so you can fully grasp the power of NexJ’s ruleset technology.
Each rule has the following format:
(rule <rule-name> (when <condition> <action1> ... <actionN>))
Expressions of the form:
(this'<attribute-name>), (@ <attr>) or (@ attr1 ... attrN)
can be used to get the value of an attribute on the instance to which the rules are applied.
(var'<variable-name>)
is used to get the value of a custom variable.
The actions can either set the value of an attribute or a custom variable, which have the following format
(this'<attribute-name> <expression>) or (var'<var-name> <expression>)
, or an action can execute arbitrary code, which must not modify the values of the attributes or variables specified in the rules actions:
(action expr1 ... exprN)
Ways of interacting with rules:
The rules engine may be directly invoked on a ruleset using:
#SysRulesEngine.invoke(obj name attribute . attributes): any
which invokes the rules engine for a specified object, rule set name and attributes
The value of an attribute may use the (rules function:
(rules name): macro
which computes the value of the current attribute using a specified rule set. This macro can be used only in a class/aspect attribute value or initializer. It takes an argument of the rule set name, and returns any computed values on the instance. e.g. (rules "default_address")
Here is an example problem from the Wikipedia article on backward chaining rule sets.
The following metadata represent a solution to these conditions. Place the following in a rullset called animals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<RuleSet description= "Test of the rules engine."> <Rule condition="(= (this'animal) "frog")" name= "frogcolor"> <Script><![CDATA[(this'colour "green")]]> </Script> </Rule> <Rule condition="(= (this'animal) "canary")" name= "canarycolor"> <Script><![CDATA[(this'colour "yellow")]]> </Script> </Rule> <Rule condition="(= (this'action) "croaks")" name= "croaks"> <Script><![CDATA[(this'animal "frog")]]> </Script> </Rule> <Rule condition="(= (this'action) "chirps")" name= "chirps"> <Script><![CDATA[(this'animal "canary")]]> </Script> </Rule> </RuleSet> |
… and place the following metadata in a class called Animal
1 2 3 4 5 6 7 |
<Class> <Attributes> <Attribute name="colour" type="string" value="(rules "animals")"/> <Attribute name="action" type="string" value="(rules "animals")"/> <Attribute name="animal" type="string" value="(rules "animals")"/> </Attributes> </Class> |
You can see below, how the rules engine can determine what all the attributes from just one of the other attributes. This is a simple example, and can solve difficult AI problems when provided with more and multiple conditions and variables.
(define obj (Animal'new (: action "chirps")))
(format "This must be a {0} {1} which {2}." (obj'colour) (obj'animal) (obj'action))
> ; "This must be a yellow canary which chirps."
(define obj (Animal'new (: colour "green")))
(format "This must be a {0} {1} which {2}." (obj'colour) (obj'animal) (obj'action))
> ; "This must be a green frog which croaks."
(define obj (Animal'new (: animal "canary")))
(format "This must be a {0} {1} which {2}." (obj'colour) (obj'animal) (obj'action))
> ; "This must be a yellow canary which chirps."