How can I make conditional rule in CLIPS to find the output
For example
(deftemplate holiday
(slot hotel (allowed-symbols nice good poor))
(slot weather (allowed-symbols sunny raining))
)
(deftemplate output
(slot option (allowed-symbols go plan stay))
)
Whith this this, how do we create a rule like
if hotel = poor then stay
if hotel = poor and weather = raining then stay
if (hotel = poor and weather = sunny) or (hotel = good and weather = raining) then plan
Thanks
(defrule hotel-rule1
(holiday (hotel ?hotel&:(eq ?hotel poor)))
=>
(assert (output (option stay)))
)
(defrule hotel-rule2
(holiday (hotel ?hotel&:(eq ?hotel poor)) (weather ?weather&:(eq ?weather raining)))
=>
(assert (output (option stay)))
)
I would split the "or" condition of your last rule in two different rules, similar to the examples I wrote.
Bye
Nic
Related
The following Clara rule code inserts a fact by providing values for all the attributes of the fact type:
(defrule vips-200-promotion
"This rule creates a special promotion for VIPs that spend +$200"
[Customer (= true vip) (= ?customerId customerId)]
[Order (> total 200) (= ?customerId customerId)]
=>
(insert! (Promotion. "PROMO-XMAS" ?customerId)));;; Inserts a Promotion fact with promoId = PROMO-XMAS and customerId = value in ?customerId
(defquery get-promos
[]
[?promotion <- Promotion]).
But how can a fact, which has previously been bind, be inserted without providing values for its attributes (since you have the whole fact with values in the bind variable)?
Like here:
(defrule vips-promotion
"This rule creates a special promotion for VIPs"
[?customer <- Customer (= true vip) (= ?customerId customerId)]
=>
(insert! (Customer. ?customer)) ;;; This line fails since insert! is expecting values for all the fact's attribute, but I want to just pass ?customer, which contains the value of the matched Customer.
(defquery get-customers
[]
[?customerx <- customer])
Minor modification, adding <- to the binding:
(defrule vips-promotion
"This rule creates a special promotion for VIPs"
[?customer <- Customer (= true vip) (= ?customerId customerId)]
=>
(insert! (Customer. ?customer))
The simple answer here seems that you would have to add a Copy constructor to the Customer class.
On a side note, a rule like that mentioned above would loop forever as the rhs(Right Hand Side) insert would then trigger the lhs(Left Hand Side) which would then trigger the rhs and repeating forever due to the nature of forward chaining rules engines.
I'm attempting to use the clojure pantomime library to extract/ocr text from a large number of tif documents (among others).
My plan has been to use pmap for to apply the mapping over a sequence of input data (from a postgres database) and then update that same postgres database with the tika/tesseract OCR output. This has been working ok, however i notice in htop that many of the cores are idle at times.
Is there anyway to reconcile this, and what steps can i take to determine why this may be blocking somewhere? All processing occurs on a single tif file, and each thread is entirely mutually exclusive.
Additional info:
some tika/tesseract processes take 3 seconds, others take up to 90 seconds. Generally speaking, tika is heavily CPU bound. I have ample memory available according to htop.
postgres has no locking issues in session management, so i don't think thats holding me up.
maybe somewhere future's are waiting to deref? how to tell where?
Any tips appreciated, thanks. Code added below.
(defn parse-a-path [{:keys [row_id, file_path]}]
(try
(let [
start (System/currentTimeMillis)
mime_type (pm/mime-type-of file_path)
file_content (-> file_path (extract/parse) :text)
language (pl/detect-language file_content)
]
{:mime_type mime_type
:file_content file_content
:language language
:row_id row_id
:parse_time_in_seconds (float (/ ( - (System/currentTimeMillis) start) 100))
:record_status "doc parsed"})))
(defn fetch-all-batch []
(t/info (str "Fetching lazy seq. all rows for batch.") )
(jdbc/query (db-connection)
["select
row_id,
file_path ,
file_extension
from the_table" ]))
(defn update-a-row [{:keys [row_id, file_path, file_extension] :as all-keys}]
(let [parse-out (parse-a-path all-keys )]
(try
(doall
(jdbc/execute!
(db-connection)
["update the_table
set
record_last_updated = current_timestamp ,
file_content = ? ,
mime_type = ? ,
language = ? ,
parse_time_in_seconds = ? ,
record_status = ?
where row_id = ? "
(:file_content parse-out) ,
(:mime_type parse-out) ,
(:language parse-out) ,
(:parse_time_in_seconds parse-out) ,
(:record_status parse-out) ,
row_id ])
(t/debug (str "updated row_id " (:row_id parse-out) " (" file_extension ") "
" in " (:parse_time_in_seconds parse-out) " seconds." )))
(catch Exception _ ))))
(dorun
(pmap
#(try
(update-a-row %)
(catch Exception e (t/error (.getNextException e)))
)
fetch-all-batch )
)
pmap runs the map function in parallel on batches of (+ 2 cores), but preserves ordering. This means if you have 8 cores, a batch of 10 items will be processed, but the new batch will only be started if all 10 have finished.
You could create your own code that uses combinations of future, delay and deref, which would be good academic exercise. After that, you can throw out your code and start using the claypoole library, which has a set of abstractions that cover the majority of uses of future.
For this specific case, use their unordered pmap or pfor implementations (upmap and upfor), which do exactly the same thing pmap does but do not have ordering; new items are picked up as soon as any one item in the batch is finished.
In situations where IO is the main bottleneck, or where processing times can greatly vary between items of work, it is the best way to parallelize map or for operations.
Of course you should take care not to rely on any sort of ordering for the return values.
(require '[com.climate.claypoole :as cp])
(cp/upmap (cp/ncpus)
#(try
(update-a-row %)
(catch Exception e (t/error (.getNextException e)))
)
fetch-all-batch )
I had a similar problem some time ago. I guess that you are making the same assumptions as me:
pmap calls f in parallel. But that doesn't mean that the work is shared equally. As you said, some take 3 seconds whereas other take 90 seconds. The thread that finished in 3 seconds does NOT ask the other ones to share some of the work left to do. So the finished threads just wait iddle until the last one finishes.
you didn't describe exactly how is your data but I will assume that you are using some kind of lazy sequence, which is bad for parallel processing. If your process is CPU bounded and you can hold your entire input in memory then prefer the use of clojure.core.reducers ('map', 'filter' and specially 'fold') to the use of the lazy map, filter and others.
In my case, these tips drop the processing time from 34 to a mere 8 seconds. Hope it helps
I tried reading tutorials about Jess, but I can't find anything very helpful. I want to build a program which finds out which instrument I'm talking about.
So, if an instrument has strings, we know that the instrument is either in the strings or percussion (i.e. piano) category. How would I write a rule that saves a fact saying the category is either percussion or strings based on this criteria?
I considered bind, but doesn't bind mean that I would have to have a separate variable for each potential category? Or, should I use an assert?
This demonstrates how to insert a fact from within a rule to store a set of possible categories.
(deftemplate Instrument (slot strings))
(deftemplate Classification (multislot category))
(defrule cat-by-strings
?i <- (Instrument (strings ?s&:(> ?s 0)))
=>
(assert (Classification (category STRING PERCUSSION)))
)
(assert (Instrument (strings 18)))
(run)
(facts)
Output:
f-0 (MAIN::initial-fact)
f-1 (MAIN::Instrument (strings 18))
f-2 (MAIN::Classification (category STRING PERCUSSION))
For a total of 3 facts in module MAIN.
Using bound variables is useless as they are limited to the context of a rule.
Using the google appengine datastore, is there a way to perform a gql query that specifies a WHERE clause on a StringProperty datatype that is case insensitive? I am not always sure what case the value will be in. The docs specify that the where is case sensitive for my values, is there a way to make this insensitive?
for instance the db Model would be this:
from google.appengine.ext import db
class Product(db.Model):
id = db.IntegerProperty()
category = db.StringProperty()
and the data looks like this:
id category
===================
1 cat1
2 cat2
3 Cat1
4 CAT1
5 CAT3
6 Cat4
7 CaT1
8 CAT5
i would like to say
gqlstring = "WHERE category = '{0}'".format('cat1')
returnvalue = Product.gql(gqlstring)
and have returnvalue contain
id category
===================
1 cat1
3 Cat1
4 CAT1
7 CaT1
I don't think there is an operator like that in the datastore.
Do you control the input of the category data? If so, you should choose a canonical form to store it in (all lowercase or all uppercase). If you need to store the original case for some reason, then you could just store two columns - one with the original, one with the standardized one. That way you can do a normal WHERE clause.
The datastore doesn't support case insensitive comparisons, because you can't index queries that use them (barring an index that transforms values). The solution is to store a normalized version of your string in addition to the standard one, as Peter suggests. The property classes in the AETycoon library may prove helpful, in particular, DerivedProperty.
This thread was helpful and makes me want to contribute with similar approach to make partial search match possible. I add one more field on datastore kind and save each word on normalized phrase as a set and then use IN filter to collide. This is an example with a Clojure. Normalize part should easy translate to java at least (thanks to #raek on #clojure), while database interaction should be convertable to any language:
(use '[clojure.contrib.string :only [split lower-case]])
(use '[appengine-magic.services.datastore :as ds])
; initialize datastore kind entity
(ds/defentity AnswerTextfield [value, nvalue, avalue])
; normalize and lowercase a string
(defn normalize [string-to-normalize]
(lower-case
(apply str
(remove #(= (Character/getType %) Character/NON_SPACING_MARK)
(java.text.Normalizer/normalize string-to-normalize java.text.Normalizer$Form/NFKD)))))
; save original value, normalized value and splitted normalized value
(defn textfield-save! [value]
(ds/save!
(let [nvalue (normalize value)]
(ds/new* AnswerTextfield [value nvalue (split #" " nvalue)]))))
; normalized search
(defn search-normalized [value]
(ds/query :kind AnswerTextfield
:filter [(= :nvalue (normalize value))]))
; partial normalized word search
(defn search-partial [value]
(flatten
(let [coll []]
(for [splitted-value (split #" " (normalize value))]
(merge coll
(ds/query :kind AnswerTextfield
:filter [(in :avalue [splitted-value])]))))))
I have read ALL on mailchimp website from all api docs to blog comments :D
and even talked (chat) with there support.
Support helped me in some why but nothing i didnt knew :D
Merge tags have IF and ELSEIF statements and work on OR logic there is NO AND !!!
A bit blaaahhhhh if you ask me... ;)
(Mailchimp add AND) XD
So i have a question if anyone tried this.
I have one List with 2 Groups
(Group title: Categories
with Group names: language course, IT course, first aid course, cooking course;
Group title: Cities
with Group names: New York, Washington, Springfield, Texas)
And i want to send campaign to users who have this criteria:
Categories:IT course
OR AND
Cities: New York
i have tried:
|INTERESTED:Categories:IT course|
|INTERESTED:Cities:New York|
(STEP 1) This text will print only to New York & IT Course Subscribers;
|END:INTERESTED:|
This text will print only to IT Course Subscribers;
BUT if they already are receiving from step ONE (STEP 1)
This will duplicate the msg
|END:INTERESTED:|
So i tried this:
|INTERESTED:Categories:IT course|
|INTERESTED:Cities:New York|
(STEP 1) This text will print only to New York & IT Course Subscribers;
|END:INTERESTED:|
*|ELSE:|*
BUT this is okey :D but what if they dont have IT course selected
where will the "Cities:New York" message print ??? SOOO.... next step xD
|END:INTERESTED:|
It should be something like this. Right?
|INTERESTED:Categories:IT course|
|INTERESTED:Cities:New York|
(STEP 1) This text will print only to New York & IT Course Subscribers;
|END:INTERESTED:|
*|ELSE:|*
|INTERESTED:Categories:IT course|
(STEP 2) This text will print only to IT course Subscribers;
|END:INTERESTED:|
*|ELSE:|*
|INTERESTED:Cities:New York|
(STEP 3) This text will print only to New York Subscribers;
and this gets duplicated but it shouldnt even get executed
|END:INTERESTED:|
*|END:INTERESTED:|*
AND COMPLEX step
What if i have one Categories and two Cities
|INTERESTED:Categories:IT course|
|INTERESTED:Cities:New York, Washington|
(STEP 1) This text will print only to New York, Washington & IT Course Subscribers;
|END:INTERESTED:|
*|END:INTERESTED:|*
how to do this one ???
:D
I think this is possible but i cant find any mistakes in nesting.
I hope you have better idea and NESTING plan ;)
And did anyone tried |IF:| on |INTERESTED:...|
a have seen in doc. that there is |ELSE| clause but not |IF| nor |ELSEIF| so i asked the support and they told me to try it (my conclusion THEY DONT KNOW) where i was very surprised, so i tried it and of course the syntax is wrong.
What did i tried?
few variations:
this is the right syntax (normal)
|INTERESTED:...|
*|END:INTERESTED:|*
|INTERESTED:...|
...
|ELSE:|
...
|END:INTERESTED:|
.
.
.
i have tried:
|IF:INTERESTED:...|
...
|ELSEIF:|
...
|END:INTERESTED:|
and
|IF:INTERESTED:...|
...
|ELSEIF:|
...
|ENDIF:|
because i didnt know what to close INTERESTED merge tag or IF merge tag
i got some gibberish but i think somethig is wrong.
Clarification: Are you trying: 1) to create a segment to send a campaign to; or 2) Cause particular information to appear in a campaign depending on which groups the subscriber is in?
Here is what you can use to control what text appears within a campaign
*|INTERESTED:Categories:IT course|*
*|INTERESTED::Cities:New York|*This text will appear only if category is "IT Course" and cities is "New York"
*|ELSE:|*This text will appear only if category is "IT Course" and cities is not "New York"
*|END:INTERESTED|*
*|ELSE:|*This text will appear only if category is not "IT Course"
*|END:INTERESTED|*
You can nest both Group inclusion (INTERESTED ... END:INTERESTED) and regular field IF:field= and END:IF sections
I have found the solution if anyone is interested here it is :D
|INTERESTED:Categories: IT course>|
|INTERESTED:Cities: New York|
Categories: IT course & Cities: New York were selected
|ELSE:|
|INTERESTED:Categories: IT course|
this is because if you dont find users
with Categories: IT course & Cities: New York
|END:INTERESTED|
|END:INTERESTED|
*|ELSE:|*
|INTERESTED:Cities: New York|
This will print if Categories: IT course were FALSE
|END:INTERESTED|
*|END:INTERESTED|*
But now i have different question, it involves all of this.
How can now SKIP sending email to person who doesnt have anything select ???
:D
My friend suggested to try with segments and API but i would also like to hear from other people.