home *** CD-ROM | disk | FTP | other *** search
/ Current Shareware 1994 January / SHAR194.ISO / cad_util / alsps.zip / ALSP9.DOC < prev    next >
Lisp/Scheme  |  1993-11-04  |  72KB  |  1,754 lines

  1. AutoLISP Tutorial #9
  2.  
  3.  
  4. Conditional Branching, Part Two:
  5.  
  6.    More AutoLISP branching functions.
  7.    Symbolic Logic.
  8.    Predicates.
  9.    Logical Operators.
  10.  
  11. Contributed by Tony Tanzillo,
  12. A/E Automation Systems.
  13.  
  14. Recap:
  15.  
  16. In part one, installment #8 in this series, we were introduced to
  17. conditional branching. We used the AutoLISP IF conditional branching
  18. function in several excercises that helped illustrate how we can make
  19. program flow branch into one of several possible paths of execution,
  20. causing them to respond differently to various results of a test or
  21. state.
  22.  
  23. In this installment, we'll look more closely at how a program can
  24. perform various tests using PREDICATES, and how we can make use of
  25. the outcome of these tests to control program logic flow by using
  26. simple and complex tests as the branching condition.
  27.  
  28. We'll also learn how to control program flow with complex conditions
  29. by using LOGICAL OPERATORS to operate on logic in a manner similar to
  30. the way we operate on numbers, and to combine multiple logical states
  31. into a single complex condition.
  32.  
  33. Finally, we will allso be introduced to several AutoLISP primitives
  34. that do conditional branching:  COND and WHILE, and we will learn how
  35. to use the PROGN function to place several expressions where only one
  36. is expected.
  37.  
  38. For the benefit of those who are new to LISP in general, but who may
  39. have some experience in other languages, we will also attempt to
  40. simplify an important distinction of LISP, with respect to the way
  41. logic is represented and interpreted by LISP conditional branching
  42. functions.
  43.  
  44. 1. Symbolic Logic.
  45.  
  46. Every computer programming language provides a means of representing
  47. the two boolean logic states of "TRUE" and "FALSE", and this is the
  48. essence of what all conditional branching and automated reasoning is
  49. based on.  In most programming languages, numeric or integer values
  50. are used to represent logic states, where 0 represents FALSE, and any
  51. other numeric value represents TRUE.  What determines this, depends
  52. on how all conditional branching constructs of a language choose to
  53. interpret a numeric value.  Most conventional languages use numeric
  54. values explictly, to represent a state of logic.  LISP does not use
  55. numeric values to convey logic.
  56.  
  57. LISP uses what can be referred to as SYMBOLIC LOGIC.  In the context
  58. of using a value as the condition for branching, we can classify all
  59. possible values as being either NIL or NON-NIL, where the only value
  60. that can represent the FALSE state is the special symbol NIL, which
  61. itself implys the absence of a value.  This concept is a major source
  62. of confusion for those migrating to LISP with some experience in
  63. other less symbolic languages.
  64.  
  65. Aside from NIL, all other LISP values are considered to be "NON-NIL",
  66. values, regardless of what the value is, or type of data, a NON-NIL
  67. value will always be interpreted as meaning "TRUE", when it appears
  68. in a branching function as the condition.
  69.  
  70. Consider these two very similar code fragments in LISP and `C':
  71.  
  72. LISP:                              C:
  73.  
  74.    (setq x 0)                         { int x = 0;
  75.    ...                                  ...
  76.    (if x (princ "X is TRUE\n")          if(x) printf("X is TRUE\n");
  77.          (princ "X is FALSE\n")         else  printf("X is FALSE\n");
  78.    )                                  }
  79.  
  80. In both cases, the value of X is the INTEGER 0, and this value itself
  81. is the condition that determines which of the two result expressions
  82. within the IF constructs will be selected for execution.   In the 'C'
  83. example, giving X a value of 0 results in a FALSE condition.  On the
  84. other hand, in the case of the LISP example, giving X the same value
  85. would result in a TRUE condition, causing it to proclaim "X is TRUE".
  86.  
  87. This is because LISP, unlike many other languages, does not interpret
  88. the numeric value 0 as representing the logic state FALSE.
  89.  
  90. Some simple examples follow.  All of the expressions will print out
  91. "TRUE", because in each case the first argument to IF is considered
  92. to represent a TRUE state:
  93.  
  94.    (if "MOO" (print "TRUE") (print "FALSE"))   ; IN LISP, ALL
  95.                                                ;
  96.    (if 0 (print "TRUE") (print "FALSE"))       ; VALUES EXCEPT NIL
  97.                                                ;
  98.    (if 'SYMBOL (print "TRUE") (print "FALSE")) ; HAVE THE LOGICAL
  99.                                                ;
  100.    (if T (print "TRUE") (print "FALSE"))       ; VALUE OF "TRUE".
  101.  
  102. In fact, the ONLY test value that could trigger the "ELSE" expression
  103. in one of the above examples, is the symbol Nil.
  104.  
  105. Binary or "bitwise" logic.
  106.  
  107. There are other ways of representing logic in LISP.  For example, the
  108. AutoCAD SYSTEM VARIABLES that represent an ON/OFF state or setting do
  109. not return NIL and NON-NIL values.  Instead, the integer values 0 and
  110. 1 are used.  In addition, certain AutoCAD entities can posses integer
  111. data fields that represent logical "flags". This is commonly known as
  112. as binary or "bitwise" logic, and AutoLISP provides special functions
  113. for performing logical operations on these values as well.
  114.  
  115. We'll be exploring bitwise logic and the various operations that we
  116. can apply to bit-coded numbers in much greater detail in a future
  117. installment, where we will use bitwise logical operations to examine
  118. entity data, and to create boolean truth tables.  As as an exercise,
  119. we will make use of LISP bitwise logical operators in an attempt at
  120. creating a rectangle clipping function using the Cohen-Sutherland
  121. line clipping algorithm in AutoLISP.
  122.  
  123. 2. Predicates.
  124.  
  125. In order for a program to branch to different paths of execution, it
  126. must use some criteria for determining which branch should be taken.
  127. So it follows that in order to determine this criteria, some kind of
  128. test must be performed. The kinds of tests that can be performed and
  129. used as the basis for doing conditional branching are limitless in
  130. scope and complexity.  The test can be as simple as interpreting the
  131. logical value of any variable (perhaps one that was entered by the
  132. user), examining the value of an AutoCAD system variable; or it can
  133. be complex as searching for a string in a file, or an entity with a
  134. particular property value in a drawing.
  135.  
  136. There are functions in AutoLISP which are specifically designed to
  137. perform tests, and which always produce a value that indicates the
  138. outcome of the test.  In another sense, we can really think of these
  139. functions as being able to provide answers to questions which the
  140. program may require at runtime, so:
  141.  
  142. Predicates are functions that answer questions.
  143.  
  144. Usually, when a program branches to one of several possible paths of
  145. execution, the decision of which branch it is to take is based on the
  146. answer to one or more specific questions, which can be obtained by
  147. using PREDICATE functions.  Some AutoLISP predicate functions can be
  148. easily identified by their symbols, which end with the character 'P'
  149. (e.g., NumberP, ListP and MinusP).  But note that not all predicates
  150. share this characteristic.  For example, the ATOM function is also a
  151. a predicate, because it returns T if its argument is an ATOM.
  152.  
  153. All predicate functions return a value to their caller that indicates
  154. whether the test which they performed was a success or failure.  All
  155. predicates return the symbol NIL if their tests failed, representing
  156. a FALSE state.
  157.  
  158. However, if a test was a success, many predicates will return the
  159. symbol T (which is just one instance of a NON-NIL value) to represent
  160. a TRUE state.  But, returning the symbol T to indicate success is not
  161. always the case.  A predicate function may return any value except
  162. NIL, to indicate a success, and regardless of what the value is, it
  163. always represents TRUE.
  164.  
  165. For example, the AutoLISP MEMBER predicate indicates if a specified
  166. item exists in a LIST.  If the element is found in the list, MEMBER
  167. doesn't return the symbol T.  Instead, it returns the portion of the
  168. list that starts with the specified element being searched for.  This
  169. makes MEMBER much more useful than it would be if it simply returned
  170. the symbol T.  The reason why it can return a LIST and still be used
  171. as a predicate, is because when used within a conditional function
  172. like IF, any value would have the very same effect as the symbol T.
  173.  
  174. Here are some typical questions which a program may need the answers
  175. to, along with their LISP equivalents as calls to AutoLISP predicate
  176. functions which provide the answers to those questions:
  177.  
  178. Questions:                              Predicate forms:
  179. -------------------------------------------------------------------
  180. Are these two numbers equal?            (= num1 num2)
  181. Is this a string?                       (eq (type "hello") 'string)
  182. Does this string appear in this list?   (member "Z" '("X" "Y" "Z"))
  183. Is this a number?                       (numberp 12.0)
  184. Is num1 greater than num2?              (> num1 num2)
  185. Is this a list?                         (listp "FOO")
  186. Is this number negative?                (minsup -3.0)
  187. Is this number zero?                    (zerop x)
  188. Are these sorted in descending order?   (apply '> (list v1 v2 v3))
  189.  
  190. An example of a predicate function used as a branching condition:
  191.  
  192.    ; get an integer from the user, and assign it to the symbol 'VAL':
  193.  
  194.    (setq val (getint "\nEnter a positive number: "))
  195.  
  196.    ; see if the user-supplied value in VAL is negative, and if
  197.    ; so, then print a message indicating such:
  198.  
  199.    (if (minusp val)                           ;; if val is negative,
  200.        (princ "\nValue cannot be negative!")  ;; then do this,
  201.        (princ "\nValue is OK")...             ;; else do this
  202.    )
  203.  
  204. Here, the predicate MINUSP is being used to provide an answer to the
  205. question:  "Is this number negative?"
  206.  
  207. Writing your own predicates.
  208.  
  209. You've already written one predicate function, that is, if you had
  210. followed the exercise in the last installment of this series, you
  211. created the function GREATER, which is really a predicate function
  212. that answers a question (is the argument greater than 0?).  But in
  213. that case, the function provides the answer to you, in a form that
  214. you understand, but not in a form that can be interpreted and used by
  215. another program or function.  In AutoLISP, all predicate functions
  216. supply the result or outcome of the test performed in a form that can
  217. be interpreted (logically) by its caller.
  218.  
  219. It is easy to write your own predicate functions that can perform
  220. just about any kind of test you could possibly need.  What follows
  221. is a very simple example of a user-defined predicate function called
  222. EVENP, a predicate that answers the question: "Is this number EVEN?"
  223.  
  224. First, we know that any number that is EVEN is equally-divisible by
  225. two, so we can use the AutoLISP REMainder function to determine if
  226. there is a non-zero remainder left after the division.
  227.  
  228. We won't need our text editor for this so just fire up AutoCAD, and
  229. we'll define the function right on the command line:
  230.  
  231.   Command:  (defun evenp (num) (zerop (rem num 2)))
  232.   EVENP
  233.   Command:
  234.  
  235. Notice that within the body of this user-defined predicate, we are
  236. calling another AutoLISP predicate called ZEROP, which obviously
  237. answers the question: Is this value equal to ZERO?   This is often
  238. the case when we abstract new predicates, that their results can be
  239. based on the result of performing one or more tests involving one
  240. or more predicates.
  241.  
  242. In fact, the predicate ZEROP could less-effeciently be written as:
  243.  
  244.    (= num 0)
  245.  
  246. Which obviously would produce the same result as ZEROP.
  247.  
  248. Now, let's try out our new predicate function:
  249.  
  250.   Command: (evenp 3)
  251.   nil
  252.   Command: (evenp 12)
  253.   T
  254.   Command:
  255.  
  256. If the argument to EVENP is an even number, the function will return
  257. the symbol T, which is a non-nil value that serves to indicate a TRUE
  258. state.  This value can then be used by a calling expression as the
  259. condition that determines which branch of code is to be evaluated
  260. next.  Conversely, if the argument is ODD, then EVENP returns the
  261. symbol NIL, indicating a false condition.
  262.  
  263. Great!  Now suppose we also wanted to write the INVERSE predicate
  264. function to EVENP, a predicate called ODDP which is to return T if
  265. its argument is an ODD number, and NIL otherwise?
  266.  
  267. In the next section we'll be introduced to LOGICAL OPERATORS, which
  268. are functions that can operate on logical values in various ways.
  269. And, we will see how it is possible to apply a logical operator to
  270. the result of EVENP, and derive from it, the inverse predicate: ODDP.
  271.  
  272. We've already seen one example of a user-defined predicate, as well
  273. as an example of how we could use the various predicates AutoLISP
  274. provides, and we will be defining and using more predicate functions
  275. in other examples throughout this tutorial.
  276.  
  277. 3. Logical Operators.
  278.  
  279. So far, we have seen how it is possible to use IF to branch on a
  280. single condition.  In the above example, the condition was simply a
  281. test to determine if the number supplied by the user was negative.
  282.  
  283. And we have also seen that PREDICATE functions can be used as the
  284. basis or condition for branching, permitting the branching function
  285. to determine if the test condition is true, and then select a path
  286. of execution based on the answer provided by the predicate.
  287.  
  288. In LISP, a logical operator is a function that accepts either one or
  289. several "connective" logical states (represented by NIL and NON-NIL
  290. values in LISP but which are generally referred to as either TRUE or
  291. FALSE).  These functions perform a "logical operation" on true/false
  292. logic states to derive ONE new logical state as a result.
  293.  
  294. Logical operations.
  295.  
  296. There are three basic logical operations that can be performed on one
  297. or more logic states.  There are several other logical operators that
  298. can only be abstracted using a more complex combination of any two or
  299. three basic operators, but we'll stick to the basic ones here.
  300.  
  301. In terms of logical operations and the values they operate on, there
  302. are only two possible values (or "logic states") that  we can apply
  303. any logical operation to: TRUE and FALSE. All logical operations must
  304. take at least one logical value as their input (arguments), and all
  305. logical operations produce only ONE value as their output (result).
  306.  
  307. The three basic connective logical operations:
  308.  
  309.   Operator   Input                      Output
  310.   --------------------------------------------------------------------
  311.   AND        Any number of TRUE/FALSE   TRUE when ALL input states
  312.              logic states.              are TRUE, otherwise FALSE.
  313.   --------------------------------------------------------------------
  314.   OR         Any number of TRUE/FALSE   TRUE when ANY one input state
  315.              logic states.              is TRUE, otherwise FALSE.
  316.   --------------------------------------------------------------------
  317.   NOT        1 TRUE/FALSE logic state.  TRUE if the input state is
  318.                                         FALSE, or FALSE if the input
  319.                                         state is true (complement).
  320.  
  321. Later in this section, we will also define another logical operator,
  322. a variation of OR called the XOR operator (exclusive OR), which can
  323. be useful to help make composite logical operations less complex.
  324.  
  325. What do logical operators do?
  326.  
  327. If you recall from lesson 8, we saw a very simple example of how we
  328. ourselves routinely do conditional branching in our minds, to make
  329. everyday decisions:
  330.  
  331.    "If it is sunny today, I will not take
  332.     my umbrella with me to work."
  333.  
  334. A simple statement with a precedent and consequent, right?
  335.  
  336. Now, consider the following connective:
  337.  
  338.    "If it is raining, AND the temprature is below 32 degrees,
  339.     then I will drive very carefully."
  340.  
  341. How many conditions are there in the precedent?
  342.  
  343. If you see more than one state or condition, then considering the key
  344. word that makes this statement a connective, which also appears in
  345. the table above, how many of these unique conditions must be TRUE, in
  346. order for the consequent to prevail?
  347.  
  348. In the above connective, there are TWO discrete logical states that
  349. enter into a single "logical AND" operation to produce a result which
  350. becomes the condition which ultimately determines if the consequent
  351. part of the statement holds.
  352.  
  353. The AutoLISP AND function.
  354.  
  355. Syntax: (and <expr>...)
  356.  
  357. The AND function is a logical operator that takes one or more logical
  358. values as arguments and operates on them to produce a single logical
  359. result.  The operation that is performed, is quite simply, that if
  360. all the logical input states are NON-NIL (true), then the result of
  361. the operation is NON-NIL (true). If any one of the arguments is NIL
  362. (false), the result is also NIL.
  363.  
  364. So, we can use AND to permit a conditional function to determine what
  365. branch of code is to be evaluated when ALL of several logical states
  366. are TRUE.
  367.  
  368. Here are some simple examples of using AND:
  369.  
  370.    Expression                Result
  371.    --------------------------------
  372.    (and T T T NIL t)         nil
  373.    (and T nil t nil nil)     nil
  374.    (and T T nil t t)         nil
  375.    (and T T T T T)           T      <--- All arguments are NON-NIL
  376.    (and nil 3 t)             nil
  377.    (and T 4 "FOO" T)         T      <--- All arguments are NON-NIL
  378.  
  379. One important but not so obvious characteristic of AND, is that it
  380. is a 'special form', that does what is referred to as CONDITIONAL
  381. EVALUATION.  Meaning that AND may not have to evaluate all of its
  382. arguments to determine its result.  The result of any AND function is
  383. known when the first NIL value is found, and the need to continue
  384. evaluating additional arguments after this is entirely pointless, so
  385. AND will never evaluate any argument that proceeds an argument which
  386. evaluates to NIL.
  387.  
  388. This is illustrated in the above examples.  Note that the symbol `T'
  389. appears in both upper and lower case.  Upper-case T's represent the
  390. results of all arguments that must be evaluated to determine what the
  391. result of the AND expression is.  On the other hand, a lower-case 't'
  392. represents the `would-be' result of an expression that would not be
  393. evaluated, since the outcome of the entire AND expression is already
  394. known before those arguments are encountered.
  395.  
  396. One very common use of AND in AutoLISP stems from the fact that it
  397. does conditional evaluation.  This lets us arrange to have a program
  398. terminate naturally, right at the point where something may have gone
  399. astray (perhaps at the point where the user fails to supply required
  400. data that makes it impossible to continue, for example).
  401.  
  402. You can use AND to cause your program to terminate immediately, as
  403. soon as the user fails to do something that is expected of them, at
  404. any interval, or immediately after any response to any input prompt
  405. and you can do it without having to write "spaghetti code".  So, now
  406. let's take a look at how our programs can exploit special forms, and
  407. their use of conditional evaluation.
  408.  
  409. A functional example:
  410.  
  411. The AutoLISP (vertex) function which follows, can be invoked at any
  412. AutoCAD prompt where a coordinate is expected.  It will permit you to
  413. compute and use as input, the point where any two imaginary lines of
  414. infinite length, which pass thru two pairs of coordinates, intersect.
  415. This program demonstrates the use of AND as a means of preventing a
  416. program from continuing to obtain input from the user, if one of the
  417. values that was expected was either not supplied, or was an improper
  418. value.
  419.  
  420. Note that both the INPUT and ASSIGNMENT of the four coordinates is
  421. done entirely within the AND construct as subexpressions.  The reason
  422. for arranging it this way is simple:   The AND function will detect
  423. the first invalid (NIL) response to any of the four prompts AS SOON
  424. AS IT IS ENTERED, and will immediately suspend evaluation of any and
  425. all proceeding arguments.  And, since one of the side-effects of the
  426. expressions within the AND construct is the act of getting all input
  427. from the user, no additional input will be requested, and the program
  428. will just display a message indicating that a value that was expected
  429. was not properly supplied, and terminate.
  430.  
  431. This is one way to validate user input, and is especially useful when
  432. a given value supplied by the user must meet all of several specific
  433. criteria (e.g., a user-supplied number must fall within the range of
  434. two defined values, or perhaps where a specific entity type is to be
  435. selected).  For those who may not be familiar with it yet, we will in
  436. a future installment be introduced to other ways AutoLISP provides us
  437. to ensure that all user input is supplied and valid.
  438.  
  439. The VERTEX function.
  440.  
  441. (defun VERTEX (/ p1 p2 p3 p4)
  442.    (if (and (setq p1 (getpoint "\n>>First line from: "))  ; IF p1 AND p2
  443.             (setq p2 (getpoint p1 " to: "))               ; AND p3 AND p4
  444.             (setq p3 (getpoint "\n>>Second line from: ")) ; are NON-NIL,
  445.             (setq p4 (getpoint p3 " to: "))               ;
  446.        )                                                  ;
  447.        (inters p1 p2 p3 p4 nil)                           ; THEN do this,
  448.        (prompt "\n>>Must specify four points.")           ; ELSE do this.
  449.    )
  450. )
  451.  
  452. Another example:
  453.  
  454. Our second example that uses AND, will be used in another excercise
  455. later in this installment, as well as with a slightly more complex
  456. project which we'll undertake in a future installment.
  457.  
  458. This example is a user-defined PREDICATE function that indicates if a
  459. subject point lies on or within a rectangular extents defined by two
  460. points.
  461.  
  462. First, we'll look at the problem graphically:
  463.  
  464.   p1+-----------------+p2
  465.     |                 |
  466.     |        +PT      |  <-- Our predicate is to return NON-NIL if the
  467.     |                 |      point 'PT' lies on or within the rectangle
  468.     |                 |      defined by two endpoints of either diagonal
  469.     |                 |
  470.   p1+-----------------+p2
  471.  
  472. One way to find out if the point is on or within the rectangle, is to
  473. determine if the point is within range of both the X and Y ordinates
  474. of two of the four corner points forming either diagonal of the
  475. rectangle.  Therefore, what we have is two connective logical states
  476. (conditions) that must be satisfied for the compound predicate to
  477. prove to be TRUE.  Each of these conditions can easily be determined
  478. by using the >= predicate, which indicates if its arguments appear in
  479. descending order or if any two or more adjacent arguments are equal.
  480.  
  481. For example:
  482.  
  483.    (>= 12 10 8 8 5)    returns -> T   (adjacent equal values are OK)
  484.  
  485.    (>= 7 5 6)          returns -> nil (not in descending order)
  486.  
  487. So, we can now develop the two following test prototypes:
  488.  
  489.      (>= Xmax pX Xmin)   ; This indicates if the X ordinate of
  490.                          ; the point is within the X range of the
  491.                          ; rectangular extents.
  492.  
  493.      (>= Ymax pY Ymin)   ; This does the same with the Y ordinates.
  494.  
  495. In the above expressions, Xmax and Ymax are the ordinates of the
  496. upper-rightmost corner of the rectangle, and Xmin and Ymin are the
  497. ordinates of the lower-left corner.  So, if the subject point pX,pY
  498. is within the rectangle, both its X and Y ordinates must be within
  499. the X and Y ordinates of these two corner points.
  500.  
  501. Remember that a 2D point is a list of 2 reals, and so (car <point> )
  502. returns the X ordinate of a point, and (cadr <point> ) returns the Y
  503. ordinate of a point.  Now, we can use the AutoLISP (MAX) and (MIN)
  504. functions to find both the low and hi X and Y ordinates of the two
  505. points on either diagonal like this:
  506.  
  507.    ; c1 and c2 are the two points on either diagonal of the
  508.    ; rectangular extents:
  509.  
  510.    (setq x-max (max (car c1) (car c2)))    ; greater X ordinate
  511.    (setq x-min (min (car c1) (car c2)))    ; lesser X ordinate
  512.  
  513. And, we can do the same for the Y ordinate:
  514.  
  515.    (setq y-max (max (cadr c1) (cadr c2)))  ; greater Y ordinate
  516.    (setq y-min (min (cadr c1) (cadr c2)))  ; lesser Y ordinate
  517.  
  518. Now, we can plug in the X and Y ordinates of PT, which is the point
  519. we are testing to see if it lies in the extents.  First extract the
  520. X and Y ordinates from the point list:
  521.  
  522.    (setq px (car PT))    ; get the X ordinate
  523.          py (cadr PT))   ; and the Y ordinate
  524.    )
  525.  
  526. And below is our composite test, where we perform a logical AND on
  527. the result of two individual tests, requiring both of them to be
  528. TRUE (X and Y) to result in AND returning T, which becomes the new
  529. predicate's result:
  530.  
  531.    (and (>= x-max px x-min)
  532.         (>= y-max py y-min)
  533.    )
  534.  
  535. So, only if both conditions are true will AND return T, indicating
  536. that the subject point does lie on or within the rectangle.  If the
  537. first condition is NOT TRUE, then the result of AND cannot be TRUE,
  538. so there is no need to evaluate the second condition.   Now, we can
  539. "shrink-wrap" our new predicate as a user-defined function:
  540.  
  541.    ; (windowp <point> <corner1> <corner2> )
  542.    ;
  543.    ; Returns NON-NIL if <point> lies on or within the rectangular
  544.    ; extents defined by the <corner1> and <corner2>, two points on
  545.    ; either diagonal of the subject rectangle.
  546.  
  547.    (defun windowp (p c1 c2 / xmax xmin ymax ymin px py)
  548.       (setq xmax (max (car c1) (car c2))     ; high X
  549.             xmin (min (car c1) (car c2))     ; low X
  550.             ymax (max (cadr c1) (cadr c2))   ; high Y
  551.             ymin (min (cadr c1) (cadr c2))   ; low Y
  552.             px   (car p)                     ; X and Y of point
  553.             py   (cadr p)                    ; to examine.
  554.       )
  555.       (and (>= xmax px xmin)  ; both conditions must be TRUE
  556.            (>= ymax py ymin)  ; if the point lies in the extents.
  557.       )                       ; The result of AND becomes the
  558.    )                          ; result of WINDOWP.
  559.  
  560. Let's test our new predicate using a small command shell:
  561.  
  562.    (defun C:INSIDE? (/ cor1 cor2 pt)
  563.       (if (and (setq cor1 (getpoint "\nOne corner: "))
  564.                (setq cor2 (getcorner cor1 "\nOther corner: "))
  565.                (setq pnt  (getpoint "\nTest point: "))
  566.           )
  567.           (if (windowp pnt cor1 cor2)
  568.               (princ "\nPoint is on/within the specified extents.")
  569.               (princ "\nPoint is outside of the specified extents.")
  570.           )
  571.           (princ "\nInvalid, must supply three points.")
  572.       )
  573.       (princ)
  574.    )
  575.  
  576. Notice again, how the AND function is used to handle the user input,
  577. so that if a null response is detected, the program will immediately
  578. terminate without prompting for any remaining input.
  579.  
  580. The above function is written strictly with code modularity in mind
  581. and not with effeciency in mind.  So, if you needed to call WINDOWP
  582. reiteratively, to for example, compare a set of many points to the
  583. same rectangular extents, the above version of WINDOWP is really not
  584. a very effecient way to do it.
  585.  
  586. The reason is simple: When you are using the function over and over,
  587. you are passing it three arguments each time it is called, but two
  588. of the three arguments always have the same values in each iteration.
  589.  
  590. This means there is a lot of needless argument passing taking place
  591. along with computation involving these arguments within the body of
  592. the called function.  This can be dealt with by writing a simplified
  593. version of of the function that doesn't get passed the coordinates of
  594. the rectangular extents (since these are the arguments that would be
  595. the same each time you call WINDOWP to compare many coordinates to
  596. the same rectangular extents).  Instead, you can write WINDOWP so it
  597. only takes one argument, the point you are comparing to the extents,
  598. and expects to find the values computed from the two points defining
  599. the rectangle assigned to several free variables that are bound to
  600. the caller's environment.
  601.  
  602. So, here is how you could re-write WINDOWP to be much more effecient
  603. when it is called reiteratively with the same extents:
  604.  
  605.   ; (nwindowp <point> )
  606.   ;
  607.   ; A less-modular version of WINDOWP for reiterativly comparing
  608.   ; many points to the same rectangular extents.
  609.   ;
  610.   ; Determines if the point <point> lies on or within the rectangular
  611.   ; extents defined by the X and Y ordinates of two points, which are
  612.   ; expected to be in the following FREE variables:
  613.   ;
  614.   ; WPXMIN:  Lowest X ordinate of the two points on either diagonal.
  615.   ; WPXMAX:  Largest X ordinate...   ""          ""            ""
  616.   ; WPYMIN:  Lowest Y ordinate...
  617.   ; WPYMAX:  Largest Y ordinate...
  618.  
  619.   (defun nwindowp (pt)
  620.      (and (>= wpxmax (car pt) wpxmin)
  621.           (>= wpymin (cadr pt) wpxmax)
  622.      )
  623.   )
  624.  
  625. With this, in the environment from which NWINDOWP is being called,
  626. put the extents where the function expects to find them.  This need
  627. only be done ONCE, regardless of how many times NWINDOWP is called on
  628. to compare any number of points to the same rectangular extents:
  629.  
  630.   ...
  631.   (setq wpxmin (min (car p1) (car p2))
  632.         wpxmax (max (car p1) (car p2))
  633.         wpymin (min (cadr p1) (cadr p2))
  634.         wpymax (max (cadr p1) (cadr p2))
  635.   )
  636.   ...
  637.  
  638. Now, assuming the symbol POINT_LIST is assigned to list of coordinates,
  639. each of which must be compared to the extents, you could do something
  640. like this to compare them to the rectangular extents:
  641.  
  642.   (foreach point POINT_LIST
  643.      (if (nwindowp point)
  644.          (princ "\nPoint is inside extents")
  645.          (princ "\nPoint is outside extents")
  646.      )
  647.   )
  648.  
  649. The point here is to illustrate that writing functions to be modular
  650. is not always in the interest of effeciency.  This is demonstrated by
  651. the fact that when any function like WINDOWP is called reteratively,
  652. that it should not be passed arguments that produce the same value in
  653. in the function body in every single iteration, and that you should
  654. try to avoid doing computations in the body of the function when they
  655. will produce the same result in every iteration.  So, if you were to
  656. treat the arguments that would carry the same values as "constants",
  657. and instead assign them once outside of the body of the function that
  658. you are calling reiteratively, it will result in more effecient code.
  659.  
  660. Back to logical operators.
  661.  
  662. Now, we'll examine another logical operation.  Here we can also draw
  663. an analogy to the conditional branching we do every day in our minds:
  664.  
  665.   "If we catch our limit of fish, OR run out of bait,
  666.    then we can go home".
  667.  
  668. Here we have another case where conditional branching is dependant on
  669. a CONNECTIVE (two conditions).  But in this case, the OR operation is
  670. implied, meaning simply that if any ONE of the two logical states is
  671. TRUE, then the condition derived from a logical OR operation is TRUE.
  672.  
  673. Only if ALL input states are found to be FALSE, will the result of
  674. the logical OR operation also be FALSE.
  675.  
  676. The AutoLISP OR function.
  677.  
  678. Syntax:  (or <expr>...)
  679.  
  680. The OR function takes any number of arguments, and interprets them
  681. logically.  Arguments are evaluated left-to-right in the order they
  682. are supplied.  When the first argument that evaluates to a NON-NIL
  683. value is encountered, all arguments proceeing it are ignored and do
  684. not get evaluated.  If a non-nil result is found, then OR immedately
  685. suspends evaulation of additonal arguments and returns the symbol T,
  686. representing a logical TRUE state.
  687.  
  688. So, we can use OR to permit a conditional function to decide if a
  689. particular branch of code is to be evaluated if any ONE of several
  690. logical states is TRUE.
  691.  
  692. Some example calls to OR, and their results:
  693.  
  694.    Expression                Result
  695.    --------------------------------
  696.    (or T t t nil t)          T
  697.        ^
  698.    (or NIL NIL T nil t)      T
  699.                ^
  700.    (or NIL NIL NIL)          nil
  701.                 ^
  702.    (or T nil nil nil)        T
  703.        ^
  704.    (or NIL 0 nil t)          T
  705.            ^
  706.    (or NIL NIL "FOO" t)      T
  707.                  ^
  708.  
  709. Like AND, OR is also a special form that does conditional evaluation.
  710. In the above examples a caret (^) appears under each expression to
  711. show the point where evaluation would stop given those values.  The
  712. caret points to the last argument that would need to be evaluated,
  713. whose result is what determines the outcome of the OR expression.
  714. All values to the right of the caret would never be evaluated under
  715. any circumstances.
  716.  
  717. Here is a very simple example of how you can use OR to determine if
  718. a given value is equal to one of several possible values.
  719.  
  720.   ; get a character from the user, must be either of the three shown:
  721.  
  722.   (setq ax (getstring "\nEnter X, Y, or Z: ))
  723.  
  724.   ; you are interested in the upper-case conversion of only the
  725.   ; first character entered:
  726.  
  727.   (setq ax (strcase (substr ax 1 1)))
  728.  
  729.   ; See if the user entered one of the three valid characters,
  730.   ; then proceed accordingly:
  731.  
  732.   (if (or (= ax "X")
  733.           (= ax "Y")
  734.           (= ax "Z")
  735.       )
  736.       (princ "\nOK!")
  737.       (princ "\nNo good, must enter X, Y, or Z!!!")
  738.   )
  739.  
  740. The message "OK!" will be printed if the value in AX is either one
  741. of the three characters "X", "Y", OR "Z".
  742.  
  743. This is usually how the typical multiple case/value comparison might
  744. be coded in another language, using a logical OR operator.  There is
  745. nothing wrong with doing it this way, but with LISP, this operation
  746. can also be performed another way, much more effeciently than doing a
  747. logical OR of multiple equality comparisons as shown above.  Instead,
  748. it can be done using the MEMBER function:
  749.  
  750.   (member <expr> <list> )
  751.  
  752. If <expr> appears in <list> (using EQUAL as a comparison test), then
  753. MEMBER will return the portion of <list> beginnning with the first
  754. occurance of <expr>.
  755.  
  756. In contrast to the above example using OR and EQUAL:
  757.  
  758.     (if (member ax '("X" "Y" "Z"))   ; value in AX appear in the list?
  759.         (princ "\nOK!")
  760.         (princ "\nNo good, must enter X, Y, OR Z.")       ; no
  761.     )
  762.  
  763. In general, assuming each instance of <testvalue> is the same:
  764.  
  765.     (or (equal <testvalue> <value1> )
  766.         (equal <testvalue> <value2> )
  767.         (equal <testvalue> <valueN> )...
  768.     )
  769.  
  770. Could be written more effeciently as:
  771.  
  772.     (member <testvalue> (list <value1> <value2> <valueN>...) )
  773.  
  774. In the latter case of MEMBER, the greater effeciency is mainly due to
  775. the fact that the <testvalue> is a constant, which is not the case in
  776. the multiple OR-EQUAL tests.  So in LISP, the OR function may not be
  777. the best way to do a multiple CASE/VALUE selection.
  778.  
  779. So what can we do with OR in LISP?
  780.  
  781. We will now create a predicate function that can be used to determine
  782. if two rectangular extents intersect each other.
  783.  
  784. Here are two theoretical TRUE/FALSE examples illustrated graphically:
  785.  
  786.    +-------------+p2
  787.    |             |
  788.    |             |x2
  789.    |       +-----+-----+p4
  790.    |       |     |     |
  791.    |p1     |x1   |     |     <---- Two rectangles intersect, so the
  792.    +-------+-----+     |           predicate should return "TRUE".
  793.            |           |
  794.            |p3         |
  795.            +-----------+
  796.  
  797.    +-------------+p2
  798.    |             |
  799.    |             | +---+p4
  800.    |p1           | |   |     <---- Two rectangles don't intersect, so
  801.    +-------------+ |   |           the predicate should return "FALSE".
  802.                    |   |
  803.                  p3+---+
  804.  
  805. Notice that we are building on the WINDOWP predicate developed as a
  806. part of our discussion on the AND function, because we can determine
  807. if these two rectangles intersect, by determining if any ONE of the
  808. four corners of either rectangle is within the extents of the other.
  809.  
  810. There are more effecient ways of doing this, but this one was chosen
  811. because of the extensive use of predicates and logical operators, for
  812. the sake of illustration.
  813.  
  814.   ; (rxint p1 p2 p3 p4)
  815.   ;
  816.   ; Determines if two given rectangular extents intersect, where
  817.   ; both must be orthogonal to the X and Y axes of the coordinate
  818.   ; system in which their defining points are expressed.
  819.   ;
  820.   ; Arguments:  P1 and P2: diagonal corners of first rectangle.
  821.   ;             P3 and P4: diagonal corners of second rectangle.
  822.   ;
  823.   ; Returns NON-NIL (T) if the rectangles intersect, NIL otherwise.
  824.   ;
  825.   ; Requires WINDOWP, and NWINDOWP predicate functions above.
  826.  
  827.   (defun rxint (p1 p2 p3 p4 / wpxmin wpxmax wpymin wpymax p5 p6)
  828.  
  829.      ; First, since we may have to compare up to four coordinates
  830.      ; to the same rectangular extents, we know from the discussion
  831.      ; above that it would be more effecient to not have to pass the
  832.      ; rectangle corner arguments each time we call the predicate.
  833.      ; So we will call the NWINDOWP function for all but one test.
  834.      ;
  835.      ; The first thing we must do is set up the values that NWINDOWP
  836.      ; requires and assign them to the variables which the function
  837.      ; expects them to be in when it is called:
  838.  
  839.      (setq wpxmin (min (car p1) (car p2))     ; NWINDOWP expects two
  840.            wpxmax (max (car p1) (car p2))     ; X and Y ordinates to
  841.            wpymin (min (cadr p1) (cadr p2))   ; be sorted and stored
  842.            wpymax (max (cadr p1) (cadr p2))   ; in WPxxxx variables.
  843.      )
  844.  
  845.      ; We also need the other two corners of the second rectangle,
  846.      ; which we can get by cross-pairing the X and Y ordinates of
  847.      ; the two known corners:
  848.  
  849.      (setq p5 (list (car p3) (cadr p4))
  850.            P6 (list (car p4) (cadr p3))
  851.      )
  852.  
  853.      ; Now we're ready to comparare the two rectangles to each other.
  854.      ; The first thing we must do is call WINDOWP once, to compare
  855.      ; any corner of the first rectangle to the second rectangle.
  856.      ; This must be done to detect or eliminate the possiblity that
  857.      ; the first rectangle lies entirely within the second one, which
  858.      ; cannot be detected by the last four corner/extents comparisons.
  859.      ;
  860.      ; If the WINDOWP predicate is false, then we know that the first
  861.      ; rectangle does not lie within the second, and if that is the
  862.      ; case, all that remains for us to do is compare the four corners
  863.      ; of the second rectangle to the first one, to satisfy all other
  864.      ; cases of a possible intersection.
  865.      ;
  866.      ; We perform a logical OR on the result of all tests, because in
  867.      ; this case, if any one of them is successful, then the result
  868.      ; of the RXINT predicate is known, and no further evaluation is
  869.      ; necessary:
  870.  
  871.      (or (windowp p1 p3 p4)  ; If rectangle 1 is within rectangle 2,
  872.          (nwindowp p3)       ; OR, if any one corner of rectangle 2
  873.          (nwindowp p4)       ; is within rectangle 1, then return T
  874.          (nwindowp p5)       ; to indicate that the two rectangles
  875.          (nwindowp p6)       ; intersect.
  876.      )
  877.   )                          ; OR is the last expression in the body
  878.                              ; of RXINT, so its result becomesthe
  879.                              ; result of RXINT.
  880.  
  881. Excercise:
  882.  
  883. Try writing a small AutoCAD command (C:FUNCTION) to test the (RXINT)
  884. function.  It should prompt for two corners of two rectangles (four
  885. points using GETPOINT and GETCORNER), and call RXINT passing it the
  886. four corner points.  Then, using IF to branch on the result of RXINT,
  887. print out a message indicating the outcome of the predicate test.
  888.  
  889. Advanced excercise:
  890.  
  891. Re-write the RXINT function so that it returns a more useful result
  892. if an intersection is found.  That result is the two corners of the
  893. intersection of the two input rectangles, or the rectangular area
  894. that lies within BOTH rectangles that the RXINT predicate compares.
  895. The first graphic illustration above shows two such points: x1 and
  896. x2.  In a case where one rectangle is entirely within the other,
  897. RXINT should return the two diagonal corners of the inner rectangle.
  898.  
  899. Note:  Save all the functions that appear in this tutorial, as they
  900.        may be used extensively in future installments for exercises,
  901.        in which we will write more useful programs using them.
  902.  
  903. Now, we move on, to the third and last of the three basic logical
  904. operators: NOT.
  905.  
  906. Lets go back again, to the conditional used in Lesson 8:
  907.  
  908.    "If it is sunny today, I will leave my
  909.     umbrella home."
  910.  
  911. Now, consider this:
  912.  
  913.    "If it is NOT raining today, I will leave
  914.     my umbrella home."
  915.  
  916. The precedents in both of the above statements are roughly equal, or
  917. they mean roughly the same thing (really they don't but we'll use a
  918. more precise example below).
  919.  
  920. The AutoLISP NOT function.
  921.  
  922. The logical NOT operator is the simplest of the three to understand,
  923. because it takes only one logical state as input, and produces only
  924. one logical state as ouput.  And, since there are only two logical
  925. states, take a guess at what NOT does to its input state?
  926.  
  927. Right - The output state of a logical NOT is simply the complement of
  928. the input state.  In simpler terms, NOT inverses its input, so that
  929. if the input state is TRUE (any NON-NIL value), then the output state
  930. would be FALSE (always NIL), and if the input state is NIL, then the
  931. result of NOT is the symbol T.
  932.  
  933. Examples of NOT:
  934.  
  935.    Expression       Result
  936.    -----------------------
  937.    (not T)          NIL     T is a non-nil value
  938.    (not nil)        T
  939.    (not "foo")      NIL     "foo", 0, and 1 are all non-nil values
  940.    (not 0)          NIL
  941.    (not 1)          NIL
  942.  
  943. In the discussion above involving the EVENP predicate, we said that
  944. we could use one of the logical operators with EVENP to easily write
  945. the INVERSE predicate ODDP.  Here's how:
  946.  
  947.     Axiom:   Anything that is NOT FALSE, is TRUE.
  948.  
  949. So, what axiom could one apply to any number that is ODD?
  950.  
  951.     Axiom:   Any number that is ODD, is NOT EVEN.
  952.  
  953. And so it follows:
  954.  
  955.    (defun ODDP (num)
  956.      (not (evenp num))    ; return T if <num> is NOT an even number
  957.    )
  958.  
  959. And in actual operation, (oddp 5) would call (evenp 5), which would
  960. then return NIL to NOT, which indicates FALSE.  In-turn, NOT would
  961. return the complement of NIL, which is T (for TRUE).
  962.  
  963. The main reason that the NOT logical operator was saved for last, is
  964. because it often will appear in conjunction with the other two basic
  965. logical operators, AND and OR, to abstract much more complex logical
  966. operations, as we will see below.
  967.  
  968. The XOR logical operator.
  969.  
  970. The XOR logical operator is the "Exclusive OR" operator.  XOR works
  971. like this:  It takes two logical states as INPUT, and produces one
  972. logical result.  If ONLY ONE input state is TRUE, and the other is
  973. FALSE, then the output is TRUE.  If the two input states are either
  974. both TRUE, or both FALSE, then the resulting output state is FALSE.
  975.  
  976.    Expression       Result
  977.    -----------------------
  978.    (xor T T)        NIL
  979.    (xor NIL T)      T
  980.    (xor NIL NIL)    NIL
  981.    (xor "moo" NIL)  T
  982.    (xor T NIL)      T
  983.    (xor T 0)        NIL
  984.  
  985. AutoLISP does not provide a primitive XOR operator (that is, not as
  986. it applies to SYMBOLIC LOGIC, but there is a way to perform logical
  987. XOR operations on binary or "bitwise" logical values in numbers and
  988. we will look at these in a future installment).
  989.  
  990. This is no problem since we can easily define our own XOR using a
  991. slightly complex connective:
  992.  
  993.   (defun XOR (a b)
  994.      (and (or a b)          ; IF A and B complement each
  995.           (not (and a b))   ; other, then return T (true).
  996.      )
  997.   )
  998.  
  999. As you can see, the complexity of composite logical operations can
  1000. become very involved and confusing.  You will encounter situations
  1001. where you will have to combine several of the basic logical operators
  1002. to perform composite logical operations on multiple operands.
  1003.  
  1004. You'll find that adding the XOR function to your AutoLISP function
  1005. library can help to reduce the complexity of some of those composite
  1006. logical operations, and make the logic and intent of your programs
  1007. clearer.
  1008.  
  1009. Improper use of PREDICATES and LOGICAL OPERATORS.
  1010.  
  1011. Just about everyone who ever coded in LISP has used double-negatives
  1012. in their conditional branching tests at one point or another.  Here
  1013. is an example of a double-negative test expression in an IF function:
  1014.  
  1015.     (setq x <expr> )
  1016.  
  1017.     (if (not (null x))       ; <--------------- a double negative
  1018.  
  1019.         (princ "\nThe value of X is NOT NULL")
  1020.         (princ "\nThe value of X is NULL")
  1021.     )
  1022.  
  1023. The above test expression is called a double-negative, because both
  1024. NOT and NULL negate (or logically invert) their arguments.  And that
  1025. means that the logical value of any argument to the above must always
  1026. be identical to the result of (not (null x)), making this operation
  1027. entirely superflorous.
  1028.  
  1029. The (not (null x)) in the above expression is exactly the same as
  1030. doing this:
  1031.  
  1032.     (if x (princ "\nThe value of X is NON-NIL")
  1033.           (princ "\nX is NIL")
  1034.     )
  1035.  
  1036. In otherwords, both given the same value for any <expr>:
  1037.  
  1038.    <expr> = (not (null <expr> ))
  1039.  
  1040. So, the LOGICAL VALUE of any <expr> is the same as the logical value
  1041. of (not (null <expr>)) given the same <expr> value in both cases.
  1042.  
  1043. Here's why:  NULL is identical to NOT.  They are the exact same two
  1044. functions that perform the exact same operation.  And so what one is
  1045. really doing by using (not (null <expr>)) is:
  1046.  
  1047.    (not (not <expr> ))   ; NOT == NULL == NOT
  1048.  
  1049. Which obviously doesn't make any sense at all.
  1050.  
  1051. Here is another common mistake:
  1052.  
  1053.    (if (/= <expr> NIL)
  1054.        (princ "\n<expr> is TRUE")
  1055.    )
  1056.  
  1057. This should written more simply as:
  1058.  
  1059.    (if <expr> (princ "\n<expr> is TRUE"))
  1060.  
  1061. And here, just as in the previous case, the logical value of <expr>
  1062. when it appears in a conditional, will always be identical to the
  1063. logical value of (/= <expr> nil)
  1064.  
  1065. The easiest way to understand this is to understand exactly what the
  1066. IF function, and all other conditional functions are doing internally
  1067. to determine what branch to take.  The IF function simply looks at
  1068. its FIRST argument (the <test> expression), and determines if it is
  1069. equal to NIL, nothing more.  If doens't care what kind of value you
  1070. give it, it only cares whether the the value is Nil.  If it is, then
  1071. the second expression in the IF function is skipped, and if a third
  1072. expression is present, it is evaluated.
  1073.  
  1074. Here is another similar example that you might see often:
  1075.  
  1076.    (if (= NIL <expr> )
  1077.        (princ "\n<expr> is FALSE")
  1078.    )
  1079.  
  1080. Which should be written as:
  1081.  
  1082.    (if (not <expr> )
  1083.        (princ "\n<expr> is FALSE")
  1084.    )
  1085.  
  1086. These kinds of syntactical errors can actaully be attributed to not
  1087. fully understanding precisely how symbolic logic works.  In general,
  1088. a test expression in an IF, or other branching function should never
  1089. contain an equality test that attempts to compare some other value to
  1090. NIL or T.  Any test of this nature is almost entirely superflorus.
  1091.  
  1092. BOUNDP is a predicate that should be avoided.
  1093.  
  1094. In AutoLISP, there is no UNBOUND variable (a bound variable is any
  1095. symbol that is known (or "bound") to the environment, and even if a
  1096. symbol is bound to NIL, it is still BOUND).  In fact, BOUNDP serves
  1097. no real purpose in AutoLISP, and in the other dialects of LISP, its
  1098. meaning is very different.
  1099.  
  1100. Here is how BOUNDP will often be (mis)used in AutoLISP:
  1101.  
  1102.    (if (boundp <expr> )
  1103.        (princ "\n<expr> is not BOUND")
  1104.    )
  1105.  
  1106. Not true, in the strictest sense, because "not bound" means that a
  1107. symbol is unknown, and therefore has NO value at all, not even the
  1108. "value" NIL.
  1109.  
  1110. Again, the above use of BOUNDP is precisely the same as:
  1111.  
  1112.    (if <expr>
  1113.        (princ "\n<expr> is NON-NIL")
  1114.    )
  1115.  
  1116. And so, the logical values of (bound <expr> ) and just <expr> are
  1117. also precisely identical.
  1118.  
  1119. Other conditional branching functions.
  1120.  
  1121. The COND function.
  1122.  
  1123. COND, is like DO/CASE in other languages.  It permits a program to
  1124. select one of many possible branches to take depending on the result
  1125. of one of many tests, where each test is associated with one branch.
  1126.  
  1127. In reality, COND is the fundamental conditional branching function in
  1128. LISP, more basic than IF (which by the way, is very seldom used by
  1129. professional COMMON LISP programmers).  That is not to suggest that
  1130. you should not use IF, especially if it will make the logic of your
  1131. program more obvious to you and perhaps others, but there some good
  1132. reasons why COND is used more often, which we will examine in detail
  1133. later in this installment.
  1134.  
  1135. The general syntax of COND:
  1136.  
  1137.    (cond  (  <test1>  <result> <result> <result>... )
  1138.           (  <test2>  <result> <result>... )
  1139.           (  <testN>  <result>... )
  1140.           ...
  1141.    )
  1142.  
  1143. Each <test> is a test expression, just like the first argument to the
  1144. IF function is.  COND evaluates each <test> in the order they appear
  1145. until it finds one that evaluates to NON-NIL.  Then, every <result>
  1146. expression which immediately proceeds the successful <test> will be
  1147. evaluated, left to right.
  1148.  
  1149. Any number of <result> expressions can be associated with each <test>,
  1150. and all of them will be evaluated if thier <test> results in NON-NIL.
  1151.  
  1152. COND will always return the result of the last <result> expression it
  1153. evaluates.  Each <test> expression and all <result> expressions which
  1154. immediately proceed it are referred to as a CLAUSE.  There can by any
  1155. number of clauses in a single COND expression.
  1156.  
  1157. If no <test> expression in a COND evaluates to a NON-NIL value, then
  1158. COND will return NIL, and no <result> expressions will be evaluated.
  1159.  
  1160. If a <test> expression has no <result> expressions associated with it,
  1161. then the NON-NIL result of the succesful <test> expression itself is
  1162. returned by COND.
  1163.  
  1164. A "default" clause can be supplied by placing it as the LAST clause
  1165. in the expression, and supplying a literal NON-NIL value in place of
  1166. the "test" (usually the symbol T is used as the default clause test
  1167. value, but any non-nil value would have precisely the same effect).
  1168.  
  1169. Here is a simple example of how you can use COND to select a value
  1170. from several possible ones depending on which test is successful.
  1171. The function takes an integer from 1 to 5, and returns the English
  1172. equivalent:
  1173.  
  1174.     (defun say-digit (n)
  1175.        (cond (  (minsup n) (princ "\nNo negative values!"))
  1176.              (  (zerop n) (princ "zero"))
  1177.              (  (eq n 1) (princ "one"))
  1178.              (  (eq n 2) (princ "two"))
  1179.              (  (eq n 3) (princ "three"))
  1180.              (  (eq n 4) (princ "four"))
  1181.              (  (eq n 5) (princ "five"))
  1182.              (t (princ "Sorry, I can't count that high"))
  1183.        )
  1184.        (princ)
  1185.     )
  1186.  
  1187. This COND expression has EIGHT clauses, with the last one being a
  1188. DEFAULT clause.  If none of the (eq..) test expressions evaluates
  1189. to a NON-NIL result, then the remaining expressions in the default
  1190. clause will be evaluated.
  1191.  
  1192. In the above examplle, each clause has only one <result> expression,
  1193. but as we will see below, each could have any number of <result>'s.
  1194.  
  1195. Here is the sequence of expressions that are evaluated if the value
  1196. supplied to SAY-DIGIT were 3:
  1197.  
  1198.      Expression      Result
  1199.      -----------------------
  1200.      (minsup n)      Nil
  1201.      (zerop n)       Nil
  1202.      (eq n 1)        Nil
  1203.      (eq n 2)        Nil
  1204.      (eq n 3)        T        <---- COND stops evaluating <test>'s
  1205.      (princ "three") "three"        and starts evaluating <result>'s
  1206.                                            
  1207. Enter SAY-DIGIT into your text editor, load it, and give it a try:
  1208.  
  1209.    Command: (say-digit 3)
  1210.    "three"
  1211.    Command: (say-digit 1)
  1212.    "one"
  1213.    Command: (say-digit 4)
  1214.    "four"
  1215.    Command: (say-digit 8)
  1216.    "Sorry, I can't count that high"
  1217.    Command:
  1218.  
  1219. This should make the basic function of COND clear.  And, note how the
  1220. default case is evaluated when no test is successful.  COND is also a
  1221. SPECIAL FORM that does CONDITIONAL EVALUATION.  It will evaluate only
  1222. to the first test expression that results in a non-nil value and will
  1223. not evaluate any part of any proceeding clauses.  We will also learn
  1224. about a more effecient way of performing an operation like this using
  1225. another AutoLISP function called ASSOC, in a future installment.
  1226.  
  1227. One very commonplace use of COND, is to branch on a series of tests
  1228. that are performed on a value input by the user, where a command may
  1229. give a user a choice of several options or "commands", where each of
  1230. these options cause a different action to be taken by the program.
  1231.  
  1232. In the following example, we will write a TINY RPN calculator that
  1233. will let you enter numeric values (or distances) and operators right
  1234. on the command line, and have the results displayed.  We'll be using
  1235. some functions that you might not have used before, or may not be
  1236. familiar with.  Don't worry about them for now, the purpose here is
  1237. simply to show the COND function in action.  But you might find this
  1238. TINY RPN calculator to be very useful with AutoCAD, so enter it into
  1239. your text editor, and give it a try!
  1240.  
  1241.   ; First, we need a subroutine to centralize input and display
  1242.   ; the current total.  This will also help to make it easier
  1243.   ; to understand the logic of the entire program.
  1244.   ;
  1245.   ; (GETCALC) sets up the keywords that are accepted as options
  1246.   ; to distance input, then displays the current total on the
  1247.   ; command line, and waits for the user to enter a response,
  1248.   ; which can be a numeric operand to be operated on, a numeric
  1249.   ; operator (e.g,. +, -, *), or a command that causes something
  1250.   ; else to happen.  The COND function below is responsible for
  1251.   ; deciding what is to happen, depending on what value the user
  1252.   ; supplies on each input.
  1253.  
  1254.   (defun getcalc ()
  1255.      (initget 1 "+ - * / Q C")                ; initialize keywords,
  1256.      (getdist (strcat "\n" (rtos total)       ; display current total
  1257.                       " "                     ; and operator, and get
  1258.                       operator                ; input from the user
  1259.                       " "
  1260.               )
  1261.      )
  1262.   )
  1263.  
  1264.   ; C:CALC - A TINY command-line calculator for AutoCAD.
  1265.   ;
  1266.   ; Bound variables:
  1267.   ;
  1268.   ;  INPUT     User input (a real number or option keyword)
  1269.   ;  TOTAL     The current calculator register total
  1270.   ;  OPERATOR  The current numeric operator (a string)
  1271.   ;
  1272.   ; Free variables:
  1273.   ;
  1274.   ;  *TOTAL*   The running total from the last use of C:CALC
  1275.   ;            which automatically carries over to the next
  1276.   ;            invocation.
  1277.  
  1278.   (defun C:CALC ( / input total operator)
  1279.  
  1280.      ; initialize operator and total (use total
  1281.      ; from last use of program if it exists).
  1282.  
  1283.      (setq operator "+"                         ; start with addition
  1284.            total    (cond (*total*) (t 0.0))    ; use 0.0 for default
  1285.      )
  1286.  
  1287.      ; say hello
  1288.  
  1289.      (princ (strcat "\nCadCalc v1.0 - Enter operator or operand,"
  1290.                     " <C>lear or <Q>uit.\n"))
  1291.  
  1292.      ; Enter the input/processing loop, and continue while the
  1293.      ; user doesn't enter "Q" to quit:
  1294.  
  1295.      (while (/= "Q" (setq input (getcalc)))  ; while user doesn't
  1296.                                              ; enter "Q",
  1297.  
  1298.         ; branch on each possible INPUT value and dispatch
  1299.         ; to the appropriate case:
  1300.  
  1301.         (cond (  (member input                     ; Did the user
  1302.                     '("+" "-" "*" "/")             ; enter one of
  1303.                  )                                 ; these operators?
  1304.                  (setq operator input)             ; yes, assign it
  1305.               )                                    ; to OPERATOR.
  1306.  
  1307.               (  (eq "C" input)                    ; clear total?
  1308.                  (setq total 0.0)                  ; yes, do it.
  1309.               )
  1310.  
  1311.               (  (numberp input)                   ; number entered?
  1312.                  (setq total                       ; yes: perform the
  1313.                        (apply (read operator)      ; operation and
  1314.                               (list total input))) ; make the result
  1315.                                                    ; the new total.
  1316.               )
  1317.  
  1318.               (t (princ "\nInvalid response."))    ; do this in
  1319.         )                                          ; case something
  1320.      )                                             ; falls thru.
  1321.  
  1322.      ; Save the current total for next time and exit.
  1323.      ; (use "!*total* to supply last total to AutoCAD input)
  1324.  
  1325.      (setq *total* total)
  1326.      (princ)
  1327.   )
  1328.  
  1329. You can add new operators (binary and unary) to this calculator very
  1330. easily.  Each new function would require another clause in the COND
  1331. expression that dispatches each value entered by the user.  Here are
  1332. some examples of some unary operators that you could add:
  1333.  
  1334.    Unary operators:  Square      Square root
  1335.                      Truncate    Reciprocal
  1336.                      Negate      Logarithim
  1337.                      Sin/Cos/Tan Pi (constant)
  1338.  
  1339. Note that all existing operators in the calculator are binary, and
  1340. operate on both the current total, and input numeric values.  If you
  1341. were to add these unary operators, they should operate on only the
  1342. current total.  So, you must retain the last binary operator used,
  1343. since it is always the default operator used on the current total,
  1344. and any numeric value entered.  All unary operators could be handled
  1345. by a single clause in the COND, if they are the same as the names of
  1346. their conterpart AutoLISP symbols.
  1347.  
  1348. Advanced excercise:
  1349.  
  1350. Add the following MEMORY functions to the calculator:
  1351.  
  1352.   MR   Copies the current value from the memory registor
  1353.        to be used as an operand.
  1354.  
  1355.   MC   Clears (zeros) value in the memory registor.
  1356.  
  1357.   Mx   Performs operation "x" on the current total and
  1358.        current memory registor value, and updates the
  1359.        memory registor value with the result, where x
  1360.        is any valid operator, e.g.,
  1361.  
  1362.        M+   add current total to memory
  1363.        M-   subtract current total from memory
  1364.        M*   multiply memory by current total
  1365.        M/   divide memory by current total, etc.
  1366.  
  1367.  
  1368. More on conditional branching.
  1369.  
  1370. IF verses COND:  Which is better?
  1371.  
  1372. COND has several advantages over IF.  A single COND expression can do
  1373. the job of many nested IF-THEN-ELSE-IF expressions more effeciently.
  1374. The following example uses several nested IF expressions where one
  1375. COND could be used to do the same job:
  1376.  
  1377. ; FLIST command:  List a file on the text display.
  1378. ;
  1379. ; fn = filename string (input by user)
  1380. ; fh = handle of opened file
  1381. ; l  = each line of the file (a string)
  1382.  
  1383. (defun C:FLIST ( / fn fh l)
  1384.    (setq fn (getstring "\nEnter filename to LIST: ")) ; get filename
  1385.    (if (/= fn "")                                     ; IF filename /= ""
  1386.        (if (setq fh (open fn "r"))                    ; THEN IF file exists
  1387.            (progn (textscr)                           ; THEN goto textscr,
  1388.                   (while (setq line (read-line fh))   ; read each line,
  1389.                          (write-line line)            ; and print each line,
  1390.                   )
  1391.                   (close fh)                          ; and close file.
  1392.            )
  1393.            (princ "\nCan't open file for input.")     ; ELSE (2nd IF)
  1394.        )
  1395.        (princ "\nInvalid, must enter filename.")      ; ELSE (1st IF)
  1396.    )
  1397.    (princ)
  1398. )
  1399.  
  1400. The PROGN function.
  1401.  
  1402.   (progn <expr1> <exprN>...)
  1403.  
  1404. PROGN is a control function that permits any number of expressions to
  1405. appear where only ONE expression is expected or permitted.  PROGN is
  1406. frequently used within an (IF) conditional expression to permit more
  1407. than one result expression to be evaluated in the case of a success
  1408. and/or failure. The result of PROGN itself, is the result of its last
  1409. expression or argument.
  1410.  
  1411. In the above example, there are three actions that must be taken if
  1412. the file is succesfully opened:
  1413.  
  1414.    1.  The program must switch to the text screen.
  1415.    2.  Each line must be read, and printed on the display.
  1416.    3.  The file must be closed.
  1417.  
  1418. The IF function only accepts three arguments; the test expression,
  1419. one expression to evaluate if the test expression is successful, and
  1420. optionally, one expression to evaluate if the test expression fails.
  1421.  
  1422. Since the above three actions cannot be accomplished by evaluating
  1423. just one expression, they can't appear as a single result expression
  1424. (well they really can, but we need to arrange for it explicitly).
  1425.  
  1426. So, in order to evaluate all of the expressions needed to perform the
  1427. three actions above if the result of the test expression is NON-NIL
  1428. (if the file was opened for input), all expressions that are to be
  1429. evaluated can appear as arguments to the PROGN function.   That is,
  1430. they are nested within in a single expression, which satisfies the
  1431. requirement of IF, that there only be one result expression that is
  1432. to be evaluated if the test is a success.
  1433.  
  1434. Here is another simple example.  This function swaps the two values
  1435. assigned to two symbols if the first one is greater than the second:
  1436.  
  1437.   (defun swap (s1 s2)
  1438.       (setq v1 (eval s1)
  1439.             v2 (eval s2)
  1440.       )
  1441.       (if (> v1 v2)
  1442.           (progn (set s1 v2)   ; both SET expressions must be
  1443.                  (set s2 v1)   ; evaluated sequentially, if the
  1444.           )                    ; test: (> v1 v2) is TRUE.
  1445.       )
  1446.   )
  1447.  
  1448. You will find that multiple PROGN's within calls to (IF) are a major
  1449. source of mismatched parenthesis.  Also, PROGN's within IF's can make
  1450. revisions to code more difficult.  In fact, using IF instead of COND
  1451. can itself be a source of frustration whenever additional test/result
  1452. branches must be added to a construct, which would require nested IF's
  1453. to avoid major surgery or using a COND, and nesting IF expressions is
  1454. a practice that should be avoided whenever possible.
  1455.  
  1456. Consider the following version of the FLIST program above, which uses
  1457. ONE COND expression instead of TWO IF expressions and a PROGN:
  1458.  
  1459. (defun C:FLIST ( / fn fh l)
  1460.    (setq fn (getstring "\nEnter filespec to LIST: "))    ; get filename
  1461.    (cond
  1462.  
  1463.      (  (= fn "")                                        ; CR pressed?
  1464.         (princ "\nInvalid, must enter a filespec.")      ; say so and exit
  1465.      )
  1466.  
  1467.      (  (not (setq fh (open fn "r")))                    ; file NOT open?
  1468.         (princ "\nCan't open specified file for input.") ; say so and exit
  1469.      )
  1470.  
  1471.      (t (textscr)                                        ; ELSE, proceed
  1472.         (while (setq l (read-line fh))                   ; as planned.
  1473.                (write-line l)
  1474.         )
  1475.         (close fh)
  1476.      )
  1477.    )
  1478.    (princ)
  1479. )
  1480.  
  1481. What is important here, is to remember that COND can help to reduce
  1482. the depth of nested expressions in contrast to IF, and it should be
  1483. apparent by comparing the two versions of FLIST, that using COND can
  1484. make program logic flow more obvious.
  1485.  
  1486. Note that in the former example which uses IF/PROGN, the two failure
  1487. result expressions of the two IF constructs (the calls to PRINC) are
  1488. not even located near the test expressions whose result triggers them,
  1489. and this is what makes it difficult to follow the logic of a program.
  1490.  
  1491. So, we can see at least one reason why using COND instead of IF can
  1492. make life easier for us.  The other reason is due to the fact that
  1493. each COND clause can contain any number of result expressions that
  1494. are evaluated when thier associated test is true and hence, there
  1495. is no need to have to nest multiple IF-THEN/ELSE result expressions
  1496. within a PROGN.
  1497.  
  1498. Formatting progam code.
  1499.  
  1500. What also helps make your programs logic clearer, is how you format
  1501. it.  Formatting is not very important to the machine, but it is very
  1502. important to make your code easy for you to understand.  Everyone
  1503. has their own way of formatting code, and there is really no right
  1504. or wrong way to do it, more important is that you will be able to
  1505. easily follow the logic of your own programs, if your formatting is
  1506. always consistent and adheres to a few basic rules.
  1507.  
  1508. If you are trying to follow code written by someone else who uses a
  1509. different formatting style, you may find it much easier if you just
  1510. reformat the entire program in accordance to your own style.
  1511.  
  1512. More conditional branching?  Yes.  We have yet to be introduced to
  1513. one fundamental AutoLISP conditional branching function: WHILE.
  1514.  
  1515. WHILE is considered to be a conditional branching function.  It uses
  1516. a test to determine if (and how many times) it should evaluate an
  1517. unlimited number of result expressions.  However, we will not cover
  1518. WHILE in this installment.  Instead, we will explore it in detail in
  1519. the next installment which will deal with the subjects of reterative
  1520. processing loops and list processing.
  1521.  
  1522.  
  1523. Review:
  1524.  
  1525. In this installment, we learned:
  1526.  
  1527.   o  LISP uses Symbolic logic in conditional branching and differs
  1528.      somewhat from other languages in this sense.
  1529.  
  1530.   o  In LISP, a value of 0 represents TRUE when used as the TEST in
  1531.      any conditional branching construct.
  1532.  
  1533.   o  In LISP, all values have a logical value, which is interpreted
  1534.      by conditional functions and logical operators.
  1535.  
  1536.   o  Except for the special symbol NIL, all values have the logical
  1537.      value of TRUE.  Nil has a logical value of FALSE.
  1538.  
  1539.   o  A predicate is a function that can perform a test and supply
  1540.      the logical result to its caller, or just "answer a question".
  1541.  
  1542.   o  Predicates can help conditional branching functions select a
  1543.      path of execution.
  1544.  
  1545.   o  Predicates always return NIL to indicate a failure.
  1546.  
  1547.   o  Predicates can return values other than T to indicate success.
  1548.  
  1549.   o  New predicate functions can be easily defined.
  1550.  
  1551.   o  AND, OR and NOT are the three BASIC logical operators.
  1552.  
  1553.   o  Logical operators can be used to combine the results of several
  1554.      logical values produced by predicate functions into more complex
  1555.      predicates which also produce logical values.
  1556.  
  1557.   o  The XOR logical operator can be defined using AND, OR, and NOT;
  1558.      and can be used to reduce the complexity of composite logical
  1559.      operations.
  1560.  
  1561.   o  The AND function can be used to validate user input as it is
  1562.      obtained, and abort a program as soon as an invalid response
  1563.      is entered or detected without letting a program needlessly
  1564.      continue to a "dead end".
  1565.  
  1566.   o  The MEMBER function can be used to do the job that the logical
  1567.      OR operator and multiple equality tests might be used to do in
  1568.      other languages, more effeciently than the equivalent in LISP.
  1569.  
  1570.   o  Writing modular functions makes your code more re-useable, but
  1571.      it is not always effecient.
  1572.  
  1573.   o  The inverse (or complement) to any predicate function can be
  1574.      defined by applying the logical NOT operator to the result
  1575.      of the predicate.
  1576.  
  1577.   o  The PROGN function permits multiple success and/or failure
  1578.      result expressions to be evaluated within an IF expression.
  1579.  
  1580.   o  COND is like CASE or DO CASE in other languages, and can be
  1581.      used to select from many possible paths of execution, or to
  1582.      return many results, depending on the result of one of many
  1583.      tests, each of which is associated with an execution path
  1584.      or value to be returned.
  1585.  
  1586.   o  Using the COND function instead of nested IF functions and
  1587.      PROGN's can often make a program much easier to revise, and
  1588.      make a program's logic flow more obvious.
  1589.  
  1590.   o  The AND, OR, and COND functions are all special forms that do
  1591.      conditional evaluation.
  1592.  
  1593.   o  Consistent formatting conventions are an important part of
  1594.      ensuring that the logic of our programs is understandable.
  1595.  
  1596. Quiz:
  1597.  
  1598. 1.   If a language uses symbolic logic, then what does the integer
  1599.      value 0 represent when it appears as the test or determinant
  1600.      condition in a branching construct?
  1601.  
  1602. 2.   In each of the following (IF) expressions, circle the result
  1603.      expression that will be evaluated, given the arguments shown:
  1604.  
  1605.        (if 1 (princ "false") (princ "true"))
  1606.  
  1607.        (if "" (princ "true") (princ "false"))
  1608.  
  1609.        (if (and 0 1) (princ "Nope") (princ "Why not?"))
  1610.  
  1611.        (if (not 1) (princ "T") (princ "NIL"))
  1612.  
  1613.        (if 0 (princ "One") (princ "Zero"))
  1614.  
  1615.        (if (member "Y" (list "x" "y" "z"))
  1616.            (princ "It's in there!")
  1617.            (princ "Nope, it's not there")
  1618.        )
  1619.  
  1620.        (if (and (or (not 1) nil)
  1621.                 (and (equal nil (not t))
  1622.                      (not nil)
  1623.                 )
  1624.            )
  1625.            (princ "TRUE")
  1626.            (princ "FALSE")
  1627.        )
  1628.  
  1629. 3.   From the following list of function names:
  1630.  
  1631.         a.  Circle all of those which are built-in predicates:
  1632.  
  1633.         b.  Cross out any predicate function that does not return
  1634.             the symbol T to indicate success (or "TRUE").
  1635.  
  1636.         ATOM    FINDFILE  /=       WHILE  BOOLEANP
  1637.         BOOLE   READ      NUMBERP  ZEROP  POSITIVEP
  1638.         LISTP   1+        EQUAL    LOG    LOSTP
  1639.         MEMBER  ABS       LIST     ~      ANGLEP
  1640.         INTERS  >         LOGAND   LSH    FOUNDP
  1641.  
  1642. 4.   In LISP, what is the easiest way to determine if a given value
  1643.      is EQUAL to any one of several 'candidate' test values.
  1644.  
  1645. 5.   What is the easiest way to extract a portion of an existing
  1646.      list, where it begins with a specified element.
  1647.  
  1648. 6.   The AND function stops evaluating its arguments as soon as it
  1649.      encounters the first:
  1650.  
  1651.         a.  NIL result
  1652.         b.  NON-NIL result
  1653.         c.  result that produces the symbol `T'
  1654.  
  1655. 7.   The OR function stops evaluating its arguments as soon as it
  1656.      encounters the first:
  1657.  
  1658.         a.  NIL result
  1659.         b.  result that produces the symbol `T'
  1660.         c.  NON-NIL result
  1661.  
  1662. 8.   What does the NOT function do to its argument?
  1663.  
  1664. 9.   Given the following assignments, what is the result of each of
  1665.      the proceeding expressions:
  1666.  
  1667.      Assume:  (setq x 1  y 0  z nil  s "moo"  n 25.0 )
  1668.  
  1669.         (or (not x) (and x (or (not y) x)))
  1670.  
  1671.         (and (not x) (or (not s) (and x y s z)))
  1672.  
  1673.         (or s (or z n) (and x y))
  1674.  
  1675. 10.  Write the predicate function POSITIVEP, which is to return T if
  1676.      its argument is 0 or positive, and NIL otherwise.  You may NOT
  1677.      use a predicate function in the definition of POSITIVEP.
  1678.  
  1679. 11.  Write a function called SLOPE, that takes two 2D coordinates,
  1680.      and returns the analytical slope of a line between the points.
  1681.      If the line is vertical then SLOPE should return NIL.  If the
  1682.      line is horizontal, then SLOPE must return 0.0
  1683.  
  1684. 12.  Using the function SLOPE, write the predicate COLNRP, which
  1685.      takes three 2D points and returns T if the three points are
  1686.      collinear, and NIL otherwise.
  1687.  
  1688. 13.  What conditional function can be used to avoid writing several
  1689.      nested IF expressions or IF expressions containing PROGN's?
  1690.  
  1691. 14.  Assuming:  (setq x 1)     (setq s "string")
  1692.                 (setq y 0)     (setq m nil)
  1693.                 (setq z nil)   (setq l (list "1" "2" "3"))
  1694.  
  1695.      In the calls to the special forms below:
  1696.  
  1697.         a.  Circle the last expression in each AND and OR operation
  1698.             that would have to be evaluated to determine the outcome
  1699.             of the expression.
  1700.  
  1701.         b.  Indicate what the result of each expression is.
  1702.  
  1703.         c.  In the COND function, circle the last expression to be
  1704.             evaluated, whose result becomes the result of the COND.
  1705.  
  1706.      (or nil m t nil s)
  1707.  
  1708.      (and z nil l nil x)
  1709.  
  1710.      (or (not y) (or (and x "foo") nil l))
  1711.  
  1712.      (or m nil z y nil x)
  1713.  
  1714.      (cond (  (eq x 2)
  1715.               (princ "first clause is true")
  1716.               (princ "first clause, second result")
  1717.            )
  1718.  
  1719.            (  (not (listp l))
  1720.               (princ "second clause is true")
  1721.            )
  1722.  
  1723.            (  (not y)
  1724.               (princ "third clause is true")
  1725.               (princ "result 2")
  1726.               (princ "result 3")
  1727.            )
  1728.  
  1729.            (m (princ "fourth clause is true"))
  1730.  
  1731.            (  (eq (type s 'string))
  1732.               (princ "fifth clause is true")
  1733.               (princ "result expression 2")
  1734.            )
  1735.  
  1736.            (t (princ "default result 1")
  1737.               (princ "default result 2")
  1738.            )
  1739.      )
  1740.  
  1741. 15.  What is an UNBOUND SYMBOL?
  1742.  
  1743. ----------------- END of Lesson 9. -------------------
  1744.  
  1745. In lesson 10:
  1746.  
  1747. Program control:
  1748.  
  1749.     Reiterative processing loops.
  1750.     Reiterative LIST processing.
  1751.     The AutoLISP STACK and recursion.
  1752.  
  1753.  
  1754.