home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / program / compiler / clips / clip_doc / clips6.man < prev    next >
Encoding:
Text File  |  1993-09-01  |  17.1 KB  |  457 lines

  1.                 Chapter 6   How To Be In Control
  2.  
  3.  
  4.  
  5.  
  6. Up to this point, you've been learning the basic syntax of CLIPS.  Now you'll 
  7. see how to apply the syntax you've learned to more powerful and 
  8.  complex programs.  You'll also learn some new syntax for keyboard input, and 
  9. see how to compare values and generate loops.
  10.  
  11.  
  12. Let's Start Reading
  13.  
  14.  
  15. Besides matching a conditional element, there is another way that a rule can 
  16. get information.  CLIPS can read information you type from the keyboard 
  17.  using the read function.
  18.    The following example shows how (read) is used to calculate the square of a 
  19. number.  Note that no extra (crlf) is needed after the (read) 
  20.  to put the cursor on a new line.  The (read) automatically resets the cursor 
  21. to a new line.
  22.  
  23. (defrule square-number
  24.    (initial-fact)
  25. =>
  26.    (printout "number? " crlf)
  27.    (bind ?num (read))
  28.    (bind ?square (* ?num ?num))
  29.    (printout "square of " ?num " is " ?square crlf))
  30.  
  31.    Since the rule is designed to use keyboard input on the RHS, it's convenient 
  32. to trigger the rule with (initial-fact).  Otherwise, you'd have 
  33.  to make up some dummy fact to trigger the rule.
  34.    The (read) function is not a general purpose function that will read 
  35. anything you type on the keyboard.  One limitation is that (read) will 
  36.  only read one atom.  So if you try to read
  37.  
  38. this is a duck
  39.  
  40. only the first atom, "this" will be read.  To (read) all of the input. you must 
  41. enclose the input within double quotes.  Of course, once the 
  42.  input is within double quotes, it is a single literal atom.  You can't access 
  43. the individual words "this", "is", "a", and "duck" with CLIPS 
  44.  functions since an atom can't be split up in CLIPS.  To split up an atom, you 
  45. must create and call an external function.
  46.    The second limitation of (read) is that you can't input parentheses unless 
  47. they are within double quotes.  Just as you can't assert a fact 
  48.  with parentheses, you can't (read) parentheses.
  49.    Even if (read) could read multiple atoms, there would be a limitation.  
  50. Because (bind) can only bind one variable at a time, you can't read 
  51.  multiple variables in a single (bind).  To read multiple values and bind them, 
  52. you'll have to use multiple (bind) actions which each contain 
  53.  a (read).  Also, since CLIPS does not have a concatenation operator, you 
  54. cannot combine separate facts.
  55.  
  56.  
  57. Reading Your Assertions
  58.  
  59.  
  60. You can read an atom into an (assert) action.  For example, suppose you wanted 
  61. to modify the square program to give the user a choice of running 
  62.  the program again.  This is a useful feature in many programs because 
  63. otherwise the program ends after one calculation.
  64.    The following version of the square program shows how to run the program 
  65. over again by retracting and then asserting a dummy fact called start-fact 
  66.  again in the do-another rule.  Although it is possible to use initial-fact 
  67. instead of start-fact, it is poor programming because other rules 
  68.  may depend on initial-fact to trigger them.  As long as your programs don't 
  69. retract (initial-fact), it's safe to use it.  Otherwise, use your 
  70.  own fact.  For convenience, you may wish to keep a (deffacts) of (start-fact) 
  71. in a file and load it in after your rules.
  72.  
  73. (defrule square-number
  74.    (start-fact)
  75. =>
  76.    (printout "number? " crlf)
  77.    (bind ?num (read))
  78.    (bind ?square (* ?num ?num))
  79.    (printout "square of " ?num " is " ?square crlf)
  80.    (printout "Do another calculation? (y or n) " crlf)
  81.    (assert (response =(read))))
  82.  
  83. (defrule do-another
  84.    (response y)
  85.    ?initial <- (start-fact)
  86. =>
  87.    (retract ?initial)
  88.    (assert (start-fact)))
  89.  
  90.    Sample output of the program is as follows.  Try some other numbers, 
  91. especially very large and very small numbers to see how your computer 
  92.  handles their precision.
  93.  
  94. number?
  95. 2
  96. square of 2 is 4
  97. Do another calculation? (y or n)
  98. y
  99. number?
  100. 4
  101. square of 4 is 16
  102. Do another calculation? (y or n)
  103.  
  104.    Notice how the two rules work together.  This is a common method in expert 
  105. systems to generate a loop where one rule triggers another.  Since 
  106.  the flow of control in a rule-based system is not sequential, you must program 
  107. the appropriate interactions between rules.
  108.  
  109.  
  110. Control Your Loop
  111.  
  112.  
  113. There are many cases in which it's useful to repeat a calculation or other 
  114. information processing.  The common way of doing this is by setting 
  115.  up a loop.  In the previous example, you've seen how a loop is set up until 
  116. the user responds no to a question.  But there are also many situations 
  117.  in which you want a loop to terminate automatically as the result of some 
  118. test.  In the previous example, the test was asking the user whether 
  119.  they wanted to do another calculation.  In a more general sense, the test of 
  120. loop termination will be a comparison of values.
  121.    The test function provides a very powerful way to compare numbers, 
  122. variables, and strings on the LHS.  The basic syntax of test is
  123.  
  124. (test (<fun> <<arg>>))
  125.  
  126. where <fun> is a predefined function of CLIPS and <<arg>> stands for one or 
  127. more arguments required by the function.  The (test) is used as a 
  128.  conditional element on the LHS.  So a rule will only be triggered if the 
  129. (test) is satisfied.
  130.    As a simple example of a (test), consider the problem of writing a program 
  131. to generate the squares of numbers up to a maximum number.  The 
  132.  following program uses (test) in a rule to decide when to stop printing the 
  133. squares.
  134.  
  135. (defrule input-max
  136.    (start-fact)
  137. =>
  138.    (printout "number of loops? " crlf)
  139.    (bind ?max (read))
  140.    (assert (loop 0 ?max)))   ;initialize ?count to 0
  141.  
  142. (defrule print-squares
  143.    (loop ?count ?max)
  144.    (test (<= ?count ?max))   ;test if ?count is <= ?max
  145. =>
  146.    (bind ?square (* ?count ?count))
  147.    (printout "square of " ?count " is " ?square crlf)
  148.    (assert (loop =(+ ?count 1) ?max))) ;?count = ?count + 1
  149.  
  150.    The fact (loop) contains information about how many numbers have been 
  151. printed in ?count, and the maximum number, ?max, to be printed.  Notice 
  152.  that the maximum number must be included in a fact and input to print-squares 
  153. for every iteration of the loop.  While the program works, it 
  154.  is wasteful of memory.  How would you change it to reduce all the (loop) facts 
  155. generated?
  156.    The (test) checks if ?count is less than or equal to ?max using the less 
  157. than or equal function, "<=".  On the last iteration of the loop, 
  158.  ?count = ?max and so the rule will not be triggered again.  The arguments of 
  159. "<=" are ?count and ?max. Look again at the syntax of (test) described 
  160.  before, and you can now see how (test (<= ?count ?max)) matches (test (<fun> 
  161. <<arg>>).
  162.    In general, you can understand how the predefined function acts on what 
  163. follows by thinking of it following the first argument.  For example,
  164.  
  165. (<= ?count ?max)
  166.  
  167. in prefix form is like saying
  168.  
  169. (?count <= ?max)
  170.  
  171. in customary infix. 
  172.    There are many predefined function provided for you by CLIPS.  The opposite 
  173. of the predefined function is the external function or user-defined 
  174.  function.  An external function is a function that you write in C and link to 
  175. CLIPS.  For more information, see the CLIPS Reference Manual.
  176.    The following table shows the predefined functions.
  177.  
  178. Symbol     Predefined Function
  179.  
  180.                  Logical Predefined Functions
  181.  
  182.    !               not(inverse) function
  183.    &&              and function
  184.    ||              or function
  185.  
  186.  
  187.                  Comparison Predefined Functions
  188.  
  189.    =               equal(numeric) function
  190.    eq              equal(strings or numbers) function
  191.    !=              not equal function
  192.    >=              greater than or equal to function
  193.    >               greater than function
  194.    <=              less than or equal to function
  195.    <               less than function
  196.  
  197.  
  198.                  Arithmetic Predefined Functions
  199.  
  200.    /               division function
  201.    *               multiplication function
  202.    **              exponentiation function
  203.    +               addition function
  204.    -               subtraction function
  205.  
  206.    All of the Comparison Functions except "eq" will give an error message if 
  207. used to compare a number and non-numbers, called strings.  The "eq" 
  208.  function should be used for checking items whose types are not known in 
  209. advance.
  210.    The (test) can be used for more complex arguments.  However, you'll have to 
  211. get used to the prefix notation to use (test) effectively.
  212.    For example, suppose you want to see if two points have a positive slope.  
  213. In the customary infix way, we can write this as
  214.  
  215. (y2 - y1) / (x2 - x1)   > 0
  216.  
  217. In order to write this in prefix form, let's start off by thinking of the 
  218. numerator as (Y) and the denominator as (X).  So we can write the above 
  219.  as
  220.  
  221. (Y) / (X)   > 0
  222.    The prefix form for division is then
  223.  
  224. (/ (Y) (X))
  225.  
  226. since in prefix the operator comes before the argument.  Now we want to see if 
  227. the division is greater than 0.  So the prefix form is used with 
  228.  (test) as follows. 
  229.  
  230. (test (> (/ (Y) (X)) 0))
  231.  
  232.    In infix form, Y = y2 - y1.  But we want the prefix form since we're making 
  233. a prefix expression.  So (- y2 y1) will be used for (Y) and (- 
  234.  x2 x1) for (X).  Now just replace (Y) and (X) by their prefix forms to get the 
  235. final expression of whether the two points have a positive slope.
  236.  
  237. (test (> (/ (- y2 y1) (- x2 x1)) 0))
  238.  
  239.    As you become more proficient in prefix form, you'll be able to write the 
  240. prefix form automatically.  At first, it's probably best if you 
  241.  write down the steps in converting from infix to prefix until you get the hang 
  242. of it.
  243.  
  244.  
  245. Let's Be Logical
  246.  
  247.  
  248. The logical functions of (test) are very useful in expressing multiple 
  249. relationships.  For example, suppose you wanted to check for valid entry 
  250.  of numbers, such as dates.  If the month is entered as a number, it must be 
  251. greater than or equal to 1 and less than or equal to 12.  The "and" 
  252.  function "&&" can be used to express this relationship in the test
  253.  
  254. (test (&& (>= ?month 1) (<= ?month 12)))
  255.  
  256. where ?month would contain the month number.  The "&&" is formed by pressing 
  257. the "&" key twice.
  258.    Likewise an invalid month number would be one that is less than 1 or greater 
  259. than 12.  This can be expressed using the logical "or" function, 
  260.  "||", formed by pressing the "|" key twice.
  261.  
  262. (test (|| (< ?month 1) (> ?month 12)
  263.  
  264.    A program to tell whether a month is valid or invalid is shown following.  
  265. Enter and run it for some different month numbers.
  266.  
  267. (defrule ask-month-number
  268.    (start-fact)
  269. =>
  270.    (printout "Number of month? " crlf)
  271.    (bind ?number (read))
  272.    (assert (month ?number)))
  273.  
  274. (defrule valid-month
  275.    ?start-fact <- (start-fact)
  276.    ?month <- (month ?number)
  277.    (test (&& (>= ?number 1) (<= ?number 12)))
  278. =>
  279.    (printout "Valid month" crlf)
  280.    (retract ?start-fact ?month)
  281.    (assert (start-fact)))
  282.  
  283. (defrule invalid-month
  284.    ?start-fact <- (start-fact)
  285.    ?month <- (month ?number)
  286.    (test (|| (< ?number 1) (> ?number 12)))
  287. =>
  288.    (printout "Invalid month" crlf)
  289.    (retract ?start-fact ?month)
  290.    (assert (start-fact)))
  291.  
  292.    Note that after facts are no longer needed, they are retracted.  This 
  293. prevents a lot of facts from building up in memory and slowing down 
  294.  execution because CLIPS tries to match them to rules.
  295.    
  296.  
  297. Name That Month
  298.  
  299.  
  300. While it's nice to know if a month is valid, it would be even nicer if the 
  301. program would name the month.  Following is a modification of the 
  302.  program which does this.  Enter and run for some month numbers and you'll see 
  303. the month names printed out.
  304.  
  305. (defrule ask-month-number
  306.    (start-fact)
  307. =>
  308.    (printout "Number of month? " crlf)
  309.    (bind ?number (read))
  310.    (assert (month ?number)))
  311.  
  312. (defrule valid-month
  313.    ?month <- (month ?number)
  314.    (test (&& (>= ?number 1) (<= ?number 12)))
  315. =>
  316.    (printout "Valid month" crlf)
  317.    (retract ?month)
  318.    (assert (valid-month ?number)))
  319.  
  320. (defrule invalid-month
  321.    ?start-fact <- (start-fact)
  322.    ?month <- (month ?number)
  323.    (test (|| (< ?number 1) (> ?number 12)))
  324. =>
  325.    (printout "Invalid month" crlf)
  326.    (retract ?start-fact ?month)
  327.    (assert (start-fact)))
  328.  
  329. (defrule print-month
  330.    ?valid <- (valid-month ?number)
  331.    (name ?month ?number)
  332.    ?start-fact <- (start-fact)
  333. =>
  334.    (printout "Month is " ?month crlf)
  335.    (retract ?start-fact ?valid)
  336.    (assert (start-fact)))
  337.  
  338. (deffacts month-names
  339.    (name January 1)
  340.    (name February 2)
  341.    (name March 3)
  342.    (name April 4)
  343.    (name May 5)
  344.    (name June 6)
  345.    (name July 7)
  346.    (name August 8)
  347.    (name September 9)
  348.    (name October 10)
  349.    (name November 11)
  350.    (name December 12))
  351.  
  352.    The data for the month names and numbers are stored in the facts of the 
  353. (deffacts) statement.  The rule print-month is triggered when there 
  354.  is a fact (valid-month ?number) made by the rule valid-month.  Notice how the 
  355. rule valid-month was modified to assert a fact (valid-month ?number) 
  356.  which is a conditional element of the rule print-month.  The second 
  357. conditional element is the fact which matches the month name and number. 
  358.   The third conditional element is just to match the fact start-fact so that 
  359. the program does not end.
  360.    Let's talk a little now about efficiency.  A minor point, but one worth 
  361. mentioning, is the choice of names in facts.  Consider the difference 
  362.  between (valid-month ?number) and an alternative (valid month ?number).  The 
  363. (valid-month ?number) pattern tries to match two atoms, "valid-month" 
  364.  and "?number".  In contrast, the pattern (valid month ?number) must try to 
  365. match three atoms to the patterns, "valid", "month", and "?number".
  366.    So if the pattern (valid month ?number) had been used, CLIPS would have to 
  367. do more work in matching, which decreases its efficiency.  Although 
  368.  this choice of names is not a big problem in this program, it can become 
  369. aggravated if you use a lot of patterns unnecessarily.
  370.    Another factor that affects efficiency is the order in which conditional 
  371. elements are listed affects the efficiency of a program.  If the 
  372.  order was reversed like this
  373.    
  374. (name ?month ?number)
  375.    ?valid <- (valid month ?number)
  376.    ?start-fact <- (start-fact)
  377.  
  378. then CLIPS would waste time in checking to see if the rule should be put on the 
  379. agenda.
  380.    The reason for this inefficiency is that CLIPS would always find 12 matches 
  381. to the first conditional element.  Then CLIPS would go on to the 
  382.  second conditional element and find one match of the valid-month number.  
  383. Finally, it would find 1 match for the (start-fact).  So a total of 
  384.  12 x 1 x 1 = 12 matches would have to be made before the rule is triggered.   
  385. Consider now the order of conditional elements shown in the program. 
  386.   The first conditional element is (valid month ?number) and so one match is 
  387. made.  Now when CLIPS checks the second conditional element, (name 
  388.  ?month ?number), only 1 match is possible because CLIPS knows what ?number is. 
  389.  Contrast this with the case described before in which CLIPS 
  390.  found 12 matches to (name ?month ?number) because it didn't know what ?number 
  391. was and so all 12 months matched.  Finally, since there's 1 match 
  392.  to the third conditional element, (start-fact), there is a total of 1 x 1 x 1 
  393. = 1 matches required compared to the 12 matches of the other version. 
  394.     For more information on efficiency, see Appendix C of the CLIPS Reference 
  395. Manual.
  396. .PA
  397.  
  398.                             Problems
  399.  
  400.  
  401. 1.  Write a program that will generate a table of squares and cubes of numbers. 
  402.  The user should be asked the math function such as square or 
  403.  cube, the minimum and maximum numbers, and the step size for incrementing 
  404. numbers.  The program should then print out the table.  A sample format 
  405.  for an input of math function square, min = 1, max = 5, and step size = 1 is 
  406. shown below.
  407.  
  408. Number     Square
  409. 1          1
  410. 2          4
  411. 3          9
  412. 4          16
  413. 5          25
  414.  
  415.  
  416. 2.  Write a program which will test if three points are colinear.  That is, 
  417. whether all three points fall on a straight line.  The user should 
  418.  be asked to specify the x and y coordinates of the three points in the form
  419.  
  420. Point 1 x1 ?
  421. Point 2 y1 ?
  422.  
  423. and so forth.
  424.  
  425. After telling the user whether the points are colinear, the user should be 
  426. asked if they want to do another.
  427.  
  428.  
  429. 3.  Write a program to compute compound interest on a loan using the infix 
  430. formula
  431.  
  432. Interest=Principal*(1+Interest-rate/Periods/100)**(Periods*Years)
  433.  
  434.    where
  435.          Principal     : amount invested
  436.          Periods       : number of times a year
  437.                          that the money is compounded
  438.          Interest-rate : yearly percent rate
  439.             Run the program for
  440.       Principal     = 20
  441.       Periods       = 12
  442.       Years         = 366
  443.       Interest rate = 8
  444.  
  445. to see how much money the Dutch would have made if they had invested their 
  446. money rather than buying Manhatten Island for $20  in 1620.
  447.  
  448.  
  449. 4.  Write a program that tells the day of the week, i.e., Sunday, Monday, and 
  450. so forth when a user inputs the month, day, and year.  The program 
  451.  should also check for invalid entry.  For example, months should be in the 
  452. range 1 - 12.  The day of month will depend on the month.  So a day 
  453.  of 31 will be valid if the month is 1 but not if the month is 2.  The program 
  454. should also account for leap years.
  455.  
  456.  
  457.