-------------------------------------------------------------------- OR Any number of TRUE/FALSE TRUE when ANY one input state logic states. is TRUE, otherwise FALSE. -------------------------------------------------------------------- NOT 1 TRUE/FALSE logic state. TRUE if the input state is FALSE, or FALSE if the input state is true (complement). Later in this section, we will also define another logical operator, a variation of OR called the XOR operator (exclusive OR), which can be useful to help make composite logical operations less complex. What do logical operators do? If you recall from lesson 8, we saw a very simple example of how we ourselves routinely do conditional branching in our minds, to make everyday decisions: "If it is sunny today, I will not take my umbrella with me to work." A simple statement with a precedent and consequent, right? Now, consider the following connective: "If it is raining, AND the temprature is below 32 degrees, then I will drive very carefully." How many conditions are there in the precedent? If you see more than one state or condition, then considering the key word that makes this statement a connective, which also appears in the table above, how many of these unique conditions must be TRUE, in order for the consequent to prevail? In the above connective, there are TWO discrete logical states that enter into a single "logical AND" operation to produce a result which becomes the condition which ultimately determines if the consequent part of the statement holds. --- MsgPost 1.1 * Origin: CADES/ftp - 615.822.2539 analog - 615.822.0817 ISDN (1:116/32) --------------- FIDO MESSAGE AREA==> TOPIC: 146 AUTOCAD Ref: F1500002 Date: 01/04/98 From: STAN BIMSON Time: 07:59am \/To: ALL (Read 0 times) Subj: AutoLisp Lesson Part 3 of 10 The AutoLISP AND function. Syntax: (and ...) The AND function is a logical operator that takes one or more logical values as arguments and operates on them to produce a single logical result. The operation that is performed, is quite simply, that if all the logical input states are NON-NIL (true), then the result of the operation is NON-NIL (true). If any one of the arguments is NIL (false), the result is also NIL. So, we can use AND to permit a conditional function to determine what branch of code is to be evaluated when ALL of several logical states are TRUE. Here are some simple examples of using AND: Expression Result -------------------------------- (and T T T NIL t) nil (and T nil t nil nil) nil (and T T nil t t) nil (and T T T T T) T <--- All arguments are NON-NIL (and nil 3 t) nil (and T 4 "FOO" T) T <--- All arguments are NON-NIL One important but not so obvious characteristic of AND, is that it is a 'special form', that does what is referred to as CONDITIONAL EVALUATION. Meaning that AND may not have to evaluate all of its arguments to determine its result. The result of any AND function is known when the first NIL value is found, and the need to continue evaluating additional arguments after this is entirely pointless, so AND will never evaluate any argument that proceeds an argument which evaluates to NIL. This is illustrated in the above examples. Note that the symbol `T' appears in both upper and lower case. Upper-case T's represent the results of all arguments that must be evaluated to determine what the result of the AND expression is. On the other hand, a lower-case 't' represents the `would-be' result of an expression that would not be evaluated, since the outcome of the entire AND expression is already known before those arguments are encountered. One very common use of AND in AutoLISP stems from the fact that it does conditional evaluation. This lets us arrange to have a program terminate naturally, right at the point where something may have gone astray (perhaps at the point where the user fails to supply required data that makes it impossible to continue, for example). You can use AND to cause your program to terminate immediately, as soon as the user fails to do something that is expected of them, at any interval, or immediately after any response to any input prompt and you can do it without having to write "spaghetti code". So, now let's take a look at how our programs can exploit special forms, and their use of conditional evaluation. A functional example: The AutoLISP (vertex) function which follows, can be invoked at any AutoCAD prompt where a coordinate is expected. It will permit you to compute and use as input, the point where any two imaginary lines of infinite length, which pass thru two pairs of coordinates, intersect. This program demonstrates the use of AND as a means of preventing a program from continuing to obtain input from the user, if one of the values that was expected was either not supplied, or was an improper value. Note that both the INPUT and ASSIGNMENT of the four coordinates is done entirely within the AND construct as subexpressions. The reason for arranging it this way is simple: The AND function will detect the first invalid (NIL) response to any of the four prompts AS SOON AS IT IS ENTERED, and will immediately suspend evaluation of any and all proceeding arguments. And, since one of the side-effects of the expressions within the AND construct is the act of getting all input from the user, no additional input will be requested, and the program will just display a message indicating that a value that was expected was not properly supplied, and terminate. This is one way to validate user input, and is especially useful when a given value supplied by the user must meet all of several specific criteria (e.g., a user-supplied number must fall within the range of two defined values, or perhaps where a specific entity type is to be selected). For those who may not be familiar with it yet, we will in a future installment be introduced to other ways AutoLISP provides us to ensure that all user input is supplied and valid. The VERTEX function. (defun VERTEX (/ p1 p2 p3 p4) (if (and (setq p1 (getpoint "\n>>First line from: ")) ; IF p1 AND p2 (setq p2 (getpoint p1 " to: ")) ; AND p3 AND p4 (setq p3 (getpoint "\n>>Second line from: ")) ; are NON-NIL, (setq p4 (getpoint p3 " to: ")) ; ) ; (inters p1 p2 p3 p4 nil) ; THEN do this, (prompt "\n>>Must specify four points.") ; ELSE do this. ) ) Another example: Our second example that uses AND, will be used in another excercise later in this installment, as well as with a slightly more complex project which we'll undertake in a future installment. This example is a user-defined PREDICATE function that indicates if a subject point lies on or within a rectangular extents defined by two points. First, we'll look at the problem graphically: p1+-----------------+p2 | | | +PT | <-- Our predicate is to return NON-NIL if the | | point 'PT' lies on or within the rectangle | | defined by two endpoints of either diagonal | | p1+-----------------+p2 One way to find out if the point is on or within the rectangle, is to determine if the point is within range of both the X and Y ordinates of two of the four corner points forming either diagonal of the rectangle. Therefore, what we have is two connective logical states (conditions) that must be satisfied for the compound predicate to prove to be TRUE. Each of these conditions can easily be determined by using the >= predicate, which indicates if its arguments appear in descending order or if any two or more adjacent arguments are equal. For example: (>= 12 10 8 8 5) returns -> T (adjacent equal values are OK) (>= 7 5 6) returns -> nil (not in descending order) So, we can now develop the two following test prototypes: (>= Xmax pX Xmin) ; This indicates if the X ordinate of ; the point is within the X range of the ; rectangular extents. (>= Ymax pY Ymin) ; This does the same with the Y ordinates. In the above expressions, Xmax and Ymax are the ordinates of the upper-rightmost corner of the rectangle, and Xmin and Ymin are the ordinates of the lower-left corner. So, if the subject point pX,pY is within the rectangle, both its X and Y ordinates must be within the X and Y ordinates of these two corner points. Remember that a 2D point is a list of 2 reals, and so (car ) returns the X ordinate of a point, and (cadr ) returns the Y ordinate of a point. Now, we can use the AutoLISP (MAX) and (MIN) functions to find both the low and hi X and Y ordinates of the two points on either diagonal like this: ; c1 and c2 are the two points on either diagonal of the ; rectangular extents: (setq x-max (max (car c1) (car c2))) ; greater X ordinate (setq x-min (min (car c1) (car c2))) ; lesser X ordinate And, we can do the same for the Y ordinate: (setq y-max (max (cadr c1) (cadr c2))) ; greater Y ordinate (setq y-min (min (cadr c1) (cadr c2))) ; lesser Y ordinate Now, we can plug in the X and Y ordinates of PT, which is the point we are testing to see if it lies in the extents. First extract the X and Y ordinates from the point list: (setq px (car PT)) ; get the X ordinate py (cadr PT)) ; and the Y ordinate ) --- MsgPost 1.1 * Origin: CADES/ftp - 615.822.2539 analog - 615.822.0817 ISDN (1:116/32) --------------- FIDO MESSAGE AREA==> TOPIC: 146 AUTOCAD Ref: F1500003 Date: 01/04/98 From: STAN BIMSON Time: 07:59am \/To: ALL (Read 0 times) Subj: AutoLisp Lesson Part 6 of 10 (or (windowp p1 p3 p4) ; If rectangle 1 is within rectangle 2, (nwindowp p3) ; OR, if any one corner of rectangle 2 (nwindowp p4) ; is within rectangle 1, then return T (nwindowp p5) ; to indicate that the two rectangles (nwindowp p6) ; intersect. ) ) ; OR is the last expression in the body ; of RXINT, so its result becomesthe ; result of RXINT. Excercise: Try writing a small AutoCAD command (C:FUNCTION) to test the (RXINT) function. It should prompt for two corners of two rectangles (four points using GETPOINT and GETCORNER), and call RXINT passing it the four corner points. Then, using IF to branch on the result of RXINT, print out a message indicating the outcome of the predicate test. Advanced excercise: Re-write the RXINT function so that it returns a more useful result if an intersection is found. That result is the two corners of the intersection of the two input rectangles, or the rectangular area that lies within BOTH rectangles that the RXINT predicate compares. The first graphic illustration above shows two such points: x1 and x2. In a case where one rectangle is entirely within the other, RXINT should return the two diagonal corners of the inner rectangle. Note: Save all the functions that appear in this tutorial, as they may be used extensively in future installments for exercises, in which we will write more useful programs using them. Now, we move on, to the third and last of the three basic logical operators: NOT. Lets go back again, to the conditional used in Lesson 8: "If it is sunny today, I will leave my umbrella home." Now, consider this: "If it is NOT raining today, I will leave my umbrella home." The precedents in both of the above statements are roughly equal, or they mean roughly the same thing (really they don't but we'll use a more precise example below). The AutoLISP NOT function. The logical NOT operator is the simplest of the three to understand, because it takes only one logical state as input, and produces only one logical state as ouput. And, since there are only two logical states, take a guess at what NOT does to its input state? Right - The output state of a logical NOT is simply the complement of the input state. In simpler terms, NOT inverses its input, so that if the input state is TRUE (any NON-NIL value), then the output state would be FALSE (always NIL), and if the input state is NIL, then the result of NOT is the symbol T. Examples of NOT: Expression Result ----------------------- (not T) NIL T is a non-nil value (not nil) T (not "foo") NIL "foo", 0, and 1 are all non-nil values (not 0) NIL (not 1) NIL In the discussion above involving the EVENP predicate, we said that we could use one of the logical operators with EVENP to easily write the INVERSE predicate ODDP. Here's how: Axiom: Anything that is NOT FALSE, is TRUE. So, what axiom could one apply to any number that is ODD? Axiom: Any number that is ODD, is NOT EVEN. And so it follows: (defun ODDP (num) (not (evenp num)) ; return T if is NOT an even number ) And in actual operation, (oddp 5) would call (evenp 5), which would then return NIL to NOT, which indicates FALSE. In-turn, NOT would return the complement of NIL, which is T (for TRUE). The main reason that the NOT logical operator was saved for last, is because it often will appear in conjunction with the other two basic logical operators, AND and OR, to abstract much more complex logical operations, as we will see below. The XOR logical operator. The XOR logical operator is the "Exclusive OR" operator. XOR works like this: It takes two logical states as INPUT, and produces one logical result. If ONLY ONE input state is TRUE, and the other is FALSE, then the output is TRUE. If the two input states are either both TRUE, or both FALSE, then the resulting output state is FALSE. Expression Result ----------------------- (xor T T) NIL (xor NIL T) T (xor NIL NIL) NIL (xor "moo" NIL) T (xor T NIL) T (xor T 0) NIL AutoLISP does not provide a primitive XOR operator (that is, not as it applies to SYMBOLIC LOGIC, but there is a way to perform logical XOR operations on binary or "bitwise" logical values in numbers and we will look at these in a future installment). This is no problem since we can easily define our own XOR using a slightly complex connective: (defun XOR (a b) (and (or a b) ; IF A and B complement each (not (and a b)) ; other, then return T (true). ) ) As you can see, the complexity of composite logical operations can become very involved and confusing. You will encounter situations where you will have to combine several of the basic logical operators to perform composite logical operations on multiple operands. You'll find that adding the XOR function to your AutoLISP function library can help to reduce the complexity of some of those composite logical operations, and make the logic and intent of your programs clearer. Improper use of PREDICATES and LOGICAL OPERATORS. Just about everyone who ever coded in LISP has used double-negatives in their conditional branching tests at one point or another. Here is an example of a double-negative test expression in an IF function: (setq x ) (if (not (null x)) ; <--------------- a double negative (princ "\nThe value of X is NOT NULL") (princ "\nThe value of X is NULL") ) The above test expression is called a double-negative, because both NOT and NULL negate (or logically invert) their arguments. And that means that the logical value of any argument to the above must always be identical to the result of (not (null x)), making this operation entirely superflorous. The (not (null x)) in the above expression is exactly the same as doing this: (if x (princ "\nThe value of X is NON-NIL") (princ "\nX is NIL") ) In otherwords, both given the same value for any : = (not (null )) So, the LOGICAL VALUE of any is the same as the logical value of (not (null )) given the same value in both cases. --- MsgPost 1.1 * Origin: CADES/ftp - 615.822.2539 analog - 615.822.0817 ISDN (1:116/32)