Call one rule from another in clips - rules

I wanted to know if there is any possibility of firing a rule from the RHS of another rule.
for example,
rule 1:
(defrule printHello
=>
(printout t " Hello World" crlf ))
rule 2:
(defrule printName
=>
/* tigger rule 1 */)

Expert systems are knowledge representation engines. They use knowledge (static and dynamic) to represent their internal state. In CLIPS, dynamic knowledge is represented with facts.
You can use a custom dedicated fact for that.
(defrule printHello
(print hello)
=>
(printout t " Hello World" crlf))
(defrule printName
=>
(assert (print hello)))

Related

How to add multiple variable in CLIPS?

I wanted to add and print the total quantity of three Products from facts. Also i need to perform a check too, on product 1 if the quantity is greater than 1.
(defrule sum_of_quantity
(or
(Product (productNumber 1)(quantity ?quantity0))
(Product (productNumber 2)(quantity ?quantity1))
(Product (productNumber 3)(quantity ?quantity2)))
=>
(bind ?totalQuantity (integer(+ ?quantity0 ?quantity1 ?quantity2)))
(printout t "TotalQuantity is " ?totalQuantity crlf))
I am actually new to clips and is finding it difficult to write rules
The or conditional element works by creating multiple rules, one for each permutation of all possible combinations of the conditional elements found within the or conditional elements in the conditions of the rule. Each of the generated rules use the same actions as the original rule. Thus your sum_of_quantity rule is translated to the following three rules (which share the same name):
(defrule sum_of_quantity
(Product (productNumber 1)(quantity ?quantity0))
=>
(bind ?totalQuantity (integer(+ ?quantity0 ?quantity1 ?quantity2)))
(printout t "TotalQuantity is " ?totalQuantity crlf))
(defrule sum_of_quantity
(Product (productNumber 2)(quantity ?quantity1))
=>
(bind ?totalQuantity (integer(+ ?quantity0 ?quantity1 ?quantity2)))
(printout t "TotalQuantity is " ?totalQuantity crlf))
(defrule sum_of_quantity
(Product (productNumber 3)(quantity ?quantity2)))
=>
(bind ?totalQuantity (integer(+ ?quantity0 ?quantity1 ?quantity2)))
(printout t "TotalQuantity is " ?totalQuantity crlf))
Looking at the generated rules you can see that there are variables in the actions of the rules that are not bound in the conditions of the rules. So when using the or conditional element, any variables referenced in the actions of the rule must be bound in each permutation.
Some rule-based language provide a "collect" conditional element that allows to easily compute summations in the conditions of a rule, but unfortunately CLIPS does not provide this functionality. You can however use the do-for-all-facts query function to iterate over a group of facts in the actions of a rule:
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate Product
(slot productNumber)
(slot quantity))
CLIPS>
(defrule sum_of_quantity
(exists (Product (productNumber 1 | 2 | 3)))
=>
(bind ?totalQuantity 0)
(do-for-all-facts ((?p Product))
(or (eq ?p:productNumber 1)
(eq ?p:productNumber 2)
(eq ?p:productNumber 3))
(bind ?totalQuantity (+ ?totalQuantity ?p:quantity)))
(printout t "TotalQuantity is " ?totalQuantity crlf))
CLIPS>
(assert (Product (productNumber 1) (quantity 10))
(Product (productNumber 2) (quantity 20))
(Product (productNumber 3) (quantity 30))
(Product (productNumber 4) (quantity 40)))
<Fact-4>
CLIPS> (run)
TotalQuantity is 60
CLIPS>
In this example, the exists conditional element is used in the conditions of the rule so that there is only one activation regardless of how many Product facts exists. This rule will activate if there is any Product fact with a productNumber of 1, 2, or 3. If you wanted all productNumbers to be required, you'd write the patterns like this:
(exists (Product (productNumber 1))
(Product (productNumber 2))
(Product (productNumber 3)))
The actions of the rule use the do-for-all-facts function to iterate over each Product fact and add the quantity slot to a running total.
The sum_of_quantity rule is expressing the need of at least one product between products 1, 2, 3. Nevertheless, it's trying to sum the quantity of all three of them. This is logically incorrect.
This is what the engine is trying to tell you. The Right Hand Side (RHS) is referring to a variable (quantity1) which might not be defined once you run the rule. This is because your rule will activate even if just productNumber 1 is asserted in.
Simply remove the or condition in your rule and it will work. To see the rule activating, just assert all the missing products with quantity 0.

CLIPS Error:Template xxx does not exist for assert

CLIPS version: 6.31
language: c++ clips C API
Why am I getting this error? How should I fix this error?
[FACTRHS1] Template be-contact-model.riskLevel does not exist for assert.
Function load-facts encountered an error
The process is as follows: Firstly, I create a CLIPS environment from the full clips rule code using the ClipsEnvLoadFromString function, I will get a normal result in this CLIPS environment using the EnvLoadFactsFromString function.Next I want to copy more than one CLIPS environment,
so I save the rules in a binary image file using the EnvBsave function and then I load a new environment from a binary file using EnvBload function, and then I use the EnvLoadFactsFromString function to load the user facts.But the EnvLoadFactsFromString function return false, and the cli stdout get the error string:
[FACTRHS1] Template be-contact-model.riskLevel does not exist for assert.
Function load-facts encountered an error
The facts parameter of the EnvLoadFactsFromString function as following:
(appId "TEST")
(be-contact-model.riskLevel "PASS")
(be-contact-model.score 0)
(channel "POST_TEXT.RlokQwRlVjUrTUlkIqOg.COMMENT")
(constantKey "constantKey")
(contact.model "contact_detector(GO)")
(contact.nicknameResult.has_contact FALSE)
(contact.nicknameResult.has_qq FALSE)
(contact.nicknameResult.has_tel FALSE)
(contact.nicknameResult.has_url FALSE)
(contact.nicknameResult.has_wechat FALSE)
(contact.riskLevel "PASS")
(contact.score 0)
(contact.textResult.baidusearch.REJECT_LEVEL 0)
(contact.textResult.has_contact FALSE)
(contact.textResult.has_qq FALSE)
(contact.textResult.has_tel FALSE)
(contact.textResult.has_url FALSE)
(contact.textResult.has_wechat FALSE)
Once you load a binary image, you can't create any new constructs. Ordered facts and patterns (those that don't have a corresponding deftemplate construct) automatically create a deftemplate. If your rules haven't already created this automatic deftemplate, it can't be created after you've already loaded a binary image:
CLIPS (6.31 6/12/19)
CLIPS> (bsave example.bin)
TRUE
CLIPS> (bload example.bin)
TRUE
CLIPS> (assert (be-contact-model.riskLevel "PASS"))
[FACTRHS1] Template be-contact-model.riskLevel does not exist for assert.
CLIPS>
If you have a rule that matches the ordered fact, you can assert this type of fact after you've loaded the binary image.
CLIPS> (clear)
CLIPS>
(defrule r1
(be-contact-model.riskLevel ?)
=>)
CLIPS> (bsave example.bin)
TRUE
CLIPS> (clear)
CLIPS> (bload example.bin)
TRUE
CLIPS> (assert (be-contact-model.riskLevel "PASS"))
<Fact-0>
CLIPS>
So the fact that you're getting an error message would indicate that you're attempting to assert a fact that none of your rules can match.
It looks like your facts are attribute/value pairs, so one thing you can do if you're asserting facts that no rule can match is to create a generic deftemplate for representing all of them:
CLIPS> (clear)
CLIPS> (deftemplate av (slot a) (slot v))
CLIPS> (assert (av (a be-contact-model.riskLevel) (v "PASS")))
<Fact-1>
CLIPS>

Replace-member is not functioning and simply says that defrule syntax is invalid

Hi i have written a display method to display some facts but i wish to replace all of the "}" with ")". But my program will not accept the replace-member method
I have used he replace-member earlier in my code but it does not seem to work in this instance. I have tried with $? and ? with no difference
(deftemplate sentence (multislot sent) (slot or-to-implies-done) (slot implies-to-or-done) (slot comm-or) )
some more code that creates the sentence
(Actual method that isn't working)
(defrule display
(sentence (sent $?check) (or-to-implies-done ~true) (implies-to-or-done ~true) (comm-or ~true))
(bind $?check (replace-member$ $?check "}" (sym-cat ")")))
=>
(printout t (implode$ (apply sym-cat $?check)) crlf))
I simply wish to replace all the "}" with "(" by using the replace-member method. Any suggestions are much appreciated error simply says syntax is wrong in defrule with no further info given.
Move the bind function call into the actions of the rule:
(defrule display
(sentence (sent $?check)
(or-to-implies-done ~true)
(implies-to-or-done ~true) (comm-or ~true))
=>
(bind $?check (replace-member$ $?check "}" (sym-cat ")")))
(printout t (implode$ (apply sym-cat $?check)) crlf))

In Common Lisp (SBCL), is there a way to examine the individual parts of an atom?

For example, if I had the atom 'ABCD, is there a way to determine the individual characters that make up the atom?
Those characters do not "make up an atom" (an atom is something different; roughly "not a list").
What you have there is a symbol. A symbol has a name, by which it is referred to. You can get the name of a symbol with the function symbol-name:
CL-USER > (symbol-name 'ABCD)
=> "ABCD"
(Note that both symbol-name and ABCD denote symbols, but symbol-name is evaluated whereas ABCD is not due to the quote—the above is read the same as (symbol-name (quote ABCD)).)
Symbols also have other properties, which you can get by the functions symbol-package, symbol-plist, symbol-value, or symbol-function.
For an overview, take a look at chapter 10 of the Hyperspec.
To get the list of characters in the symbol:
(coerce (string 'ABCD) 'list) ; => (#\A #\B #\C #\D)
You can perform any sort of usual string analysis on a symbol if you first convert it:
(string 'ABCD) ; => "ABCD"
(char (string 'ABCD) 0) ; => #\A
(subseq (string 'ABCD) 0 2) ; => "AB"

Hunchentoot/cl-who page composition

Hunchentoot/cl-who Page Composition
I'm trying to put together a few pages in hunchentoot as an experiment, and I'm running into an unexpected wall. As an example, I have the following template macro.
(defmacro page-template ((&key title) &body body)
`(with-html-output-to-string
(*standard-output* nil :prologue t :indent t)
(:html :xmlns "http://www.w3.org/1999/xhtml" :xml\:lang "en" :lang "en"
(:head (:meta :http-equiv "Content-Type" :content "text/html;charset=utf-8")
(:title ,(format nil "~#[~A - ~]Test Site" title)))
(:body ,#body))))
Now when I have a pure text page, or one filled with html literals like
(define-easy-handler (test-page :uri "/") ()
(page-template (:title "Splash Page") (:p "Testing testing")))
everything is a-ok. The page outputs properly and I can see te efforts of my code instantly. However, when I have a page which is made up of redundant elements, it's not as simple. For example, lets say I have a page on which for whatever reason I want to display three RSS newsfeeds. This is a sufficiently complex component that I want to abstract it out, so to my minnd, I should be able to do something like
(define-easy-handler (test-feed :uri "/feeds") ()
(page-template (:title "Splash Page")
(publish-newsfeed "http://nf-one.html")
(publish-newsfeed "http://nf-two.html")
(publish-newsfeed "http://nf-three.html")))
(defmacro publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let ((rss-feed (xmls:parse (drakma:http-request url))))
`(:div :class "rss-feed"
(:a :href ,(get-text rss-feed '("channel" "link")) :target "_top" (:h1 ,(get-text rss-feed '("channel" "title"))))
(:ul ,#(mapcar #'(lambda (item)
`(:li (:a :href ,(get-text item '("link")) :target "_top" (:h2 ,(get-text item '("title"))))
(:p :class "date" ,(get-text item '("pubDate")))
(:p ,(get-text item '("description")))))
(let ((items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item")))
(if (> (length items) item-limit) (subseq items 0 item-limit) items))))))))
But the result of the above is a "Server Error" page. I'm not quire sure why; page-template is a macro so the calls to publish-newsfeed shouldn't be expanded until they're in the context of with-html-output-to-string. Can anyone tell me what I'm doing wrong?
Also, on closer inspection of the various Hunchentoot/cl-who tutorials, none of them seems to do this kind of page composition. Can anyone with some Hunchentoot experience tell me what the correct/canonical way of decomposing a page into components is?
EDIT:
Correct response by Ramarren below; the with-html-output macros work under different evaluation rules. The version of publish-newsfeed that would actually work in this situation is actually
(defun publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let* ((rss-feed (xmls:parse (drakma:http-request url)))
(items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item"))
(ltd-items (if (> (length items) item-limit) (subseq items 0 item-limit) items)))
(with-html-output
(*standard-output* nil :indent t)
(:div :class "rss-feed"
(:a :href (get-text rss-feed '("channel" "link")) :target "_top" (:h1 (str (get-text rss-feed '("channel" "title")))))
(:ul (dolist (item ltd-items)
(htm (:li (:h2 (:a :href (get-text item '("link")) :target "_top" (str (get-text item '("title")))))
(:p :class "date" (str (get-text item '("pubDate"))))
(:p (str (get-text item '("description")))))))))))))
Note the removal of mapcar for dolist (I'm a Schemer, don't give me too much of a hard time about liking lambdas, but they weren't the right choice here), and the use of htm to escape blocks of html s-exps (h-exps?) that wouldn't otherwise be in context for with-html-output. Finally, I had to wrap text but NOT :href properties in (str ) to get them to expand dynamically.
The macro with-html-output-to-string expands its body using special evaluation rules. In particular, any not recognized forms are left as is, which means macros are not expanded before the html generating code is generated, which means by the time your publish-newsfeed macro is expanded by the standard compiler it is no longer in context ofwith-html-output-to-string. This is clear when expanding the macro manually, especially using slime macroexpansion feature.
To make it work you should make publish-newsfeed a function and use with-html-output inside it with the same stream (either assume `standard-output everywhere or pass the stream explicitly).

Resources