Control Structures
A program that always executes the same instructions in the same order is quickly limited. Control structures allow your code to make decisions and repeat actions. In AutoLISP, they follow the same prefix syntax as everything else.
Comparison Operators
Before you can make decisions, you need to be able to compare values. AutoLISP provides the following operators:
(= 5 5) ; → T (equality)
(/= 5 3) ; → T (not equal)
(< 3 5) ; → T (less than)
(> 5 3) ; → T (greater than)
(<= 3 3) ; → T (less than or equal)
(>= 5 3) ; → T (greater than or equal)
These functions return T (true) or nil (false).
Comparing Strings: Watch Out for Case!
String comparison with = is case-sensitive:
(= "hello" "hello") ; → T
(= "hello" "Hello") ; → nil (case-sensitive!)
(= "HELLO" "hello") ; → nil
This is a common pitfall, especially when comparing user input or AutoCAD entity names. To perform a case-insensitive comparison, convert both strings to uppercase (or lowercase) with strcase before comparing:
(= (strcase "hello") (strcase "Hello")) ; → T
(= (strcase "HELLO") (strcase "hello")) ; → T
Best practice: when comparing user input to an expected value, always normalize the case with
strcase.
Comparing Real Numbers: Watch Out for Rounding!
With real numbers (floating-point), comparison with = can produce surprising results due to rounding errors inherent in computer calculations:
(setq result (+ 0.1 0.2))
(= result 0.3) ; → nil (!!)
The result of (+ 0.1 0.2) is not exactly 0.3 but something like 0.30000000000000004. This is a classic problem that affects all programming languages, not just AutoLISP.
The solution is to use the equal function with a tolerance argument:
(equal (+ 0.1 0.2) 0.3 1e-10) ; → T
The third argument (1e-10, i.e., 0.0000000001) is the tolerance: if the difference between the two values is less than this tolerance, equal considers them equal.
Golden rule: never compare two real numbers with
=. Always useequalwith an appropriate tolerance. This is particularly important in CAD where coordinates are real numbers and geometric calculations accumulate rounding errors.
;; Compare two points (lists of reals) with tolerance
(setq point1 '(10.0 20.0 0.0))
(setq point2 '(10.0000001 19.9999999 0.0))
(equal point1 point2 1e-6) ; → T
equal also works with lists: it compares elements one by one with the given tolerance, making it ideal for comparing points.
Logical Operators
To combine conditions:
(and T T) ; → T (logical AND)
(and T nil) ; → nil
(or nil T) ; → T (logical OR)
(or nil nil) ; → nil
(not nil) ; → T (negation)
(not T) ; → nil
and and or accept multiple arguments:
(and (> 5 3) (< 10 20) (= 1 1)) ; → T (all conditions are true)
(or (> 1 10) (< 5 3) (= 2 2)) ; → T (at least one is true)
The if Condition
The if structure is the simplest decision-making structure:
(if condition
expression-if-true
expression-if-false
)
Example:
(setq age 25)
(if (>= age 18)
(alert "You are an adult.")
(alert "You are a minor.")
)

if Without else
The "if false" expression is optional:
(if (> temperature 100)
(alert "Warning: overheating!")
)
If the condition is false and there is no "else" branch, if returns nil.
Executing Multiple Expressions: progn
if only accepts a single expression per branch. If you need to execute several, group them with progn:
(if (>= grade 10)
(progn
(princ "\nWell done!")
(princ "\nYou passed the exam.")
(setq result "passed")
)
(progn
(princ "\nToo bad.")
(princ "\nYou will have to retake the exam.")
(setq result "failed")
)
)
progn executes a sequence of expressions and returns the value of the last one.
The Multiple Condition cond
When you have more than two cases to handle, cond is more readable than a cascade of nested if statements:
(cond
(condition1 expression1)
(condition2 expression2)
(condition3 expression3)
(T defaultExpression)
)
cond evaluates the conditions from top to bottom and executes the first one whose condition is true. The last line with T as the condition serves as a default case (since T is always true).
Example: Angle Classification
(defun c:angle-type (/ angle)
(setq angle (getreal "\nEnter an angle in degrees: "))
(cond
((= angle 0) (alert "Zero angle"))
((< angle 90) (alert "Acute angle"))
((= angle 90) (alert "Right angle"))
((< angle 180) (alert "Obtuse angle"))
((= angle 180) (alert "Straight angle"))
((< angle 360) (alert "Reflex angle"))
(T (alert "Invalid or full angle"))
)
(princ)
)

Multiple Expressions per Case
Each cond case can contain multiple expressions without needing progn:
(cond
((< grade 10)
(princ "\nFailed.")
(setq honors "insufficient")
)
((< grade 12)
(princ "\nPass.")
(setq honors "pass")
)
((< grade 14)
(princ "\nFairly good.")
(setq honors "fairly-good")
)
(T
(princ "\nGood or very good!")
(setq honors "good")
)
)
The while Loop
while repeats a block of instructions as long as a condition is true:
(while condition
expression1
expression2
...
)
Example: Countdown
(setq counter 10)
(while (> counter 0)
(princ (strcat "\n" (itoa counter) "..."))
(setq counter (- counter 1))
)
(princ "\nLiftoff!")
Result in the command line:
10...
9...
8...
...
1...
Liftoff!
Practical Example: Requesting a Valid Value
(defun c:valid-radius (/ radius)
(setq radius 0)
(while (<= radius 0)
(setq radius (getreal "\nEnter a positive radius: "))
(if (<= radius 0)
(princ "\nError: the radius must be greater than 0.")
)
)
(alert (strcat "Radius accepted: " (rtos radius 2 2)))
(princ)
)

The repeat Loop
repeat executes a block a fixed number of times:
(repeat count
expression1
expression2
...
)
Example:
(setq total 0)
(repeat 5
(setq total (+ total 10))
)
total ; → 50
Example: Drawing a Regular Polygon
(defun c:polygon (/ side-count radius center angle increment current-point)
(setq side-count (getint "\nNumber of sides: "))
(setq radius (getreal "\nRadius: "))
(setq center (getpoint "\nCenter: "))
(setq increment (/ (* 2 pi) side-count))
(setq angle 0)
;; Calculate the first point
(setq current-point
(list
(+ (car center) (* radius (cos angle)))
(+ (cadr center) (* radius (sin angle)))
)
)
(repeat side-count
(setq angle (+ angle increment))
(setq next-point
(list
(+ (car center) (* radius (cos angle)))
(+ (cadr center) (* radius (sin angle)))
)
)
;; Draw a line between the two points
(command "LINE" current-point next-point "")
(setq current-point next-point)
)
(princ)
)
This program draws a regular polygon by calculating the vertices using trigonometry. Don't worry if you don't understand all the functions used (car, cadr, list, command) — we will study them in the following chapters.
Summary
| Structure | Purpose | Syntax |
|---|---|---|
if |
Simple condition | (if test true false) |
progn |
Group expressions | (progn expr1 expr2 ...) |
cond |
Multiple conditions | (cond (test1 expr) (test2 expr) ...) |
while |
Conditional loop | (while test expr ...) |
repeat |
Fixed loop | (repeat n expr ...) |
and |
Logical AND | (and cond1 cond2) |
or |
Logical OR | (or cond1 cond2) |
not |
Negation | (not condition) |
In the next chapter, we dive into the heart of LISP: lists, the most important data structure in the language.
Need an AutoCAD (AutoLISP, ObjectARX, .NET, VBA) development? Contact me for a free quote.