AutoCAD

AutoLISP Lesson #7 - PROGRAM CONTROL
AutoLISP has many predefined functions to allow you to control your programs. Some of these functions are what are known as "relational" operators, and some are known as "conditional" operators.

Relational operators
Note: Arguments in brackets [] are optional.

= (= atom atom ...)
(= 3 2) ;Returns nil
(= 3 3) ;Returns T
(= 3 3 3) ;Returns T

Determines if a list of atoms is numerically equal to each other. This operator will work for strings as well as numbers. If you are having trouble getting two values you know are equal to return as equal you may want to use the "eq" or "equal" functions.

/= (/= atom atom ...)
(/= 3 2) ;Returns T
| (/= 3 3) ;Returns nil

Determines if two atoms are not numerically equal to each other. This operator works with strings as well as numbers. If more than two arguments are supplied to the "/=" operator the function is "undefined".

< (< atom atom ...)
(< 3 2) ;Returns nil
(< 2 3) ;Returns T
(< 2 3 4) ;Returns T

Determines if a list of atoms is numerically less than each other. This function returns true if each atom is numerically less than the atom on its right.

<= (<= atom atom ...)
(<= 3 2) ;Returns nil
(<= 2 3) ;Returns T
(<= 3 3) ;Returns T

Determines if a list of atoms is numerically less than or equal to each other. Returns true if each atom is numerically less than or equal to the atom on its right.

> (> atom atom ...)
(> 3 2) ;Returns T
(> 2 3) ;Returns nil
(> 3 3) ;Returns nil

Determines if a list of atoms is numerically greater than each other. Returns true if each atom is numerically greater than the atom on its right.

>= (>= atom atom)
(>= 3 2) ;Returns T
(>= 2 3) ;Returns nil
(>= 3 3) ;Returns T

Determines if a list of atoms is numerically greater than or equal to each other. Returns true if each atom is numerically greater than or equal to the atom on its right.

eq (eq exp1 exp2)

Determines if two expressions are identical to each other. For the expressions to be true they must be bound to the same object. If the expressions are bound to the same object the function will return true.

Example
Assignments:

(setq set1 '(a b c))
(setq set2 '(a b c))
(setq set3 set2)

Tests:

(eq set1 set3) ;Returns nil
;set1 and set3
;are not the
;same list
(eq set2 set3) ;Returns T
;set2 and set3
;are exactly
;the same list

equal (equal exp1 exp2 [fuzz])
(equal 1.0000 1.0001) ;Returns nil
(equal 1.0000 1.0001 0.001) ;Returns T

Determines if two expressions evaluate to the same thing. The two expressions don't have to be identical here. They only have to evaluate to the same value.
Two numbers or point list that look identical may or may not evaluate to being equal, that is why this function adds the "fuzz" factor to the test. You can give an amount by which the two expressions can be off from each other, and still be considered equal.

Conditional operators
if (if testexp thenexp [elseexp])

Conditional evaluates expressions. If "testexp" does not return nil, then "if" evaluates "thenexp", else it evaluates "elseexp" if given, or returns nil if no "elseexp" was given.

while (while testexp exp ...)

Evaluates the "exp" while the "testexp" remains true. While evaluates "testexp" and if "testexp" is true it evaluates all expressions enclosed in the loop. It then reevaluates "testexp", if "testexp" is still true it goes through the loop again. This continues untill "testexp" evaluates to nil.

cond (cond (test1 result1 ...) ...)

Evaluates "result1" if "test1" is found to be true. This function accepts any number of list as arguments. It evaluates the first item in each list until one of these items returns a value other than nil. When it gets a value other than nil, it evaluates the "result1" expression(s). If it gets a value other than nil from a list and there is only one expression in the list, it returns the value of the expression.

Predicates
There are also operators that are known as predicate operators to help you control your programs.

and (and exp1 exp2 ...)

Returns the logical AND of a list of expressions. If any of the expressions in the argument list evaluate to nil this function ceases operation and returns nil.

or (or exp1 exp2 ...)

Returns the logical OR of a list of expressions. This function evaluates the expressions from left to right, and upon finding an expression that evaluates to a value other than nil it ceases operation and returns "true".

atom (atom 'symbol)

Returns true if the symbol points to an atom. Otherwise nil is returned. This function evaluates the symbol and if it evaluates to anything other than a list, returns true. Anything that is not a list is an atom.

boundp (boundp 'symbol)

Returns true if the symbol has a value bound to it. If no value is bound to the symbol, it is automatically created and is bound to nil.

listp (listp symbol)

Returns true if symbol evaluates to a list, otherwise it returns nil.

minusp (minusp symbol)

Returns true if symbol is a number less than 0, otherwise returns nil.

not (not symbol)

Returns true if the symbol is nil, otherwise returns nil.

null (null symbol)

Returns true if the symbol is bound to nil, otherwise returns nil.

numberp (numberp symbol)

Returns true if the symbol is an integer or a real number, otherwise returns nil.

zerop (zerop symbol)

Returns true if the symbol is an integer or a real number and evaluates to 0, otherwise it returns nil.

Using the IF function
Probably the most used control statement in programming is the "if" statement. It's used and it's abused, but "if" used correctly it can make programming much easier. There are a few rules you should follow when coding "if" statements, some make your program better, and some just make it easier to follow the logic.
The first thing you should do when writing "if" a statement is: write the nominal path through the code first. If you write the normal flow of the program first and then code the exceptions, your program will be much clearer to anyone trying to read and understand it.

(if (= test1 true1)
(do_this1))
(if (= test2 true2)
(do_this2))

By writing the normal case first we can read through the normal flow without having to worry about what we are going to do if it turns out false. Now once we know how we want things to work we can add the code to deal with the exceptions.

(if (= test1 true1)
(do_this1)
(else_do_this1))
(if (= test2 true2)
(do_this2)
(else_do_this2))

The normal case for program flow should always follow the "if", the else statement should only be executed if the normal case is not true. This way our program doesn't have to slog through exceptions to get to what we really want to happen. It also makes it easier for maintenance programmers to tell what our logic was when we wrote the code.
Another thing to watch for when programming "if" statements are to branch correctly on equality checks. Be sure when using <, >, <=, and >= statements that the expressions you are comparing are breaking at the right point. Don't use a < when you really need a <=. It is sometimes very easy to get off by one.
Most "if" statements need an else clause, so if you write an "if" statement and think you don't need an else think about it again, you just might find you need one.

If & Boolean functions
"If" function can sometimes be simplified by using boolean function as the test statement. A boolean function is a function that either returns true or false, the false case in lisp being nil. If a boolean function returns nil the case is said to be false.
The predicate functions above are boolean type functions with the exceptions of "and" and "or". You can also make your own if the boolean function you need is not built in.
For example if I wanted to see if a letter was between a & d, and I didn't care about whether it was upper or lower case, I could write the "if" statement as follows.

(if (or (and (< (ascii symbol) 69)(> (ascii symbol) 64))
(and (< (ascii symbol) 101))(> (ascii symbol) 96)))
(do_this))

Or I could write it like this.

(if (btwn_a_d)
(do_this))

I would then write a boolean style function with all the code to check whether the string was between a & d in it. As you can see the second way of writing the function is much clearer. That's not the only thing I gain from writing it this way, I also can add code to the check
Function to make sure the input was a character and not some data type I was not looking for.

Conditional statements
The condition function in AutoLISP is the basic case statement. A case statement would be like lining up a whole bunch of "if" statements in a row. As a matter of fact this is something you should keep an eye out for. If your control module starts turning into a whole line of "if"
Statements, it's time to turn the whole thing into a condition statement.
All the statements made about boolean expressions above apply equally well to condition statements. You should always try to make the test expression something, which is either true or false. If it is something, which can easily be a true or false statement, a boolean function can be written to test it. Your code will be much easier to read and understand.
When writing conditional statements try to have a logical order to the cases. If nothing more than alphabetical it will help in understanding the code. The way you should try to do it is to make the conditions most likely to occur to be at the top of the listing. This will make your code run faster when executing.
The best way to make a condition statement clear is to try to keep the cases simple. By this I mean you should use boolean functions for the test, if it gets complicated, and use a function call for the result if the case is true. Something similar to what follows.

(cond
((is_alpha symbol) (print symbol))
((is_numeric symbol) (* symbol 2)))

Kind of a short example but I think you can see where I'm heading with this. The code above is clear and easy to read.
At the end of the condition statement you can put a default case that will evaluate if all the other cases fail. Here you can put any code you wish to run in an error trapping capacity. Usually if none of the cases work, something is wrong, and something needs to be done to fix it. You create a default statement by forcing the last list in the condition statement to be true. It is written as follows.

(cond
((is_alpha symbol) (print symbol))
((is_numeric symbol) (* symbol 2))
(T (fix_error))

Try not to use condition statements too liberally, they can really make a mess out of your code. Lists should be used for storing variable values and assigning them after the case statement finds out which case is true. Write the condition statement to test which case you have then run a function to assign variables stored somewhere else. A condition statement should be used to test conditions not assign variables.