AfraLISP - Learn AutoLISP for AutoCAD productivity


by Kenny Ramage

Written by David Hoekstra
Published on AfraLisp with kind permission.

"IF I had a nickel for every COND statement that I ever wrote in lisp, I'd be rich!"

I see lots of lisp routines that use only the IF function for conditional statements. I think this has a lot to do with the fact that most new AutoLISP programmers start out with IF, and never proceed on to COND. I have talked to several lisp programmers who state that they never learnt how to use COND, and seemed to think it was useless.

COND is a thing of beauty, and let me tell you why.

The basic syntax of COND is:

(cond ((predicate1) (then do something 1))    ;if this predicate is true,
                                              ;do something1
      ((predicate2) (then do something 2))    ;if this predicate is true,
                                              ;do something2,
                                              ;each predicate and action
                                              ;following the
                                              ;first one is optional
      (t (else do this))                      ;else, if none of the
                                              ;predicates returns
                                              ;T, do this

Compare that to the syntax of IF:
(if (only one predicate)    ;must return T, even if it is a double negative
  (then do something)       ;if the predicate is true, then do this
                            ;prescribed something
  (else do this)            ;this is optional, if there is an else, then
                            ;this is what happens
                            ;if the predicate is not true

With IF, there is only one predicate (or grouped predicate), one then (or grouped then), and one optional else (or grouped else) statement. When these are all you need, then IF works quite nicely, i.e., logically. Where I find IF to be distasteful is when it is used with grouped then or else statements; and also if you need more than one predicate, it requires several IF statements in combination.

Because IF can only accept one statement for the then and else clause, you must use the PROGN to group several statements as one statement:

(if (> angle 90)
  (progn                          ;then clause
    (setq ed (subst (cons 8 "somelayer") (assoc 8 ed) ed))
    (entmod ed)
    (entupd en)
  ) ;_ progn
  (princ "\nNothing changed.")    ;optional else clause
) ;_ if

The "then" clause needs to run several statements if the predicate is true (> angle 90), if the symbol "angle" is greater than 90. Because IF only accepts one statement for a then clause, you must artificially wrap the three statements into a grouped statement by use of PROGN. Now let's compare this to the same operation, but written with COND instead:

(cond ((< angle 90)
       (setq ed (subst (cons 8 "somelayer") (assoc 8 ed) ed))
       (entmod ed)
       (entupd en)
      ) ;_ end of the first then condition
       (princ "\nNothing changed.")
      ) ;_ end of optional else condition
) ;_ end of cond statement

Here there is no artificial wrapping of several statements into one group. This COND statement will not fail if you forget to add a PROGN wrapper! In the case above, I consider COND to be a more logical implementation, and therefore "the right thing to do." Remember, programmers live and die by the logic in their code. This is certainly one reason to consider using COND instead of IF, but let me give another one: multiple predicates in one COND statement!

(cond ((and (>= angle 0) (<= angle 90)
       (setq ed (subst (cons 8 "somelayer") (assoc 8 ed) ed))
       (entmod ed)
       (entupd en)
      ) ;_ end of the first then condition
       ((and (> angle 90) (<= angle 180)
       (statement 1)
       (statement 2)
       (statement 3)
       (statement 4)
      ) ;_ end of the second then condition
      ((and (> angle 180) (<= angle 270)
       (statement a)
       (statement b)
       (statement c)
       (statement d)
      ) ;_ end of the third then condition
      ((and (> angle 270) (< angle 360)
       (statement x)
       (statement y)
       (statement z)
       (statement 0)
      ) ;_ end of the third then condition
) ;_ end of cond statement

In this one COND statement, there are four separate conditions that could be met, the equivalent IF statement would require four separate IF statements. Furthermore, because the predicates are evaluated sequentially, you can use that to your advantage. In some cases you may not have to exactly define every detail for each predicate:

(setq angdir 354)
(cond ((< angdir 90)
       (princ "\ncond1")
      ((< angdir 180)
       (princ "\ncond2")
      ((< angdir 270)
       (princ "\ncond3")
      ((< angdir 360)
       (princ "\ncond4")
) ;_ cond

And finally, another reason to use COND as much as possible. Have you ever written a lisp routine that is entirely dependent on a condition being met right at the start? Consider putting the majority of the code inside a COND statement. Then if that condition is not met, the routine will quietly exit without the need to run the EXIT or QUIT function, which can force an error call. I might want to perform several things in a drawing, but the whole process is dependent on having a particular drawing format block inserted:

(defun C:TEST (/)
  (setq ss (ssget "X" '((0 . "insert")(2 . "e1_title"))))
  (cond (ss          ;if there is a selection set, then proceed
         (statement 1)
         (statement 2)
         ... ad infinitum
         (statement 455)
        (t           ;else exit the routine without forcing an error call
         (princ "\nNo E1_TITLE format found, cannot continue, exiting!")
  ) ;_ cond
) ;_ C:TEST

To be sure, IF statements have their place. There is no reason to write a COND statement when a simple IF statement makes more logical sense:

(if (null tfstd)
  (load "tfstd" "\nTFSTD.LSP was not found in any of the paths.")

The above example would not be very logical if it were forced into a COND statement. As we can see there are times when only an IF statement will do the trick nicely, and other times when only COND will. The problem is when AutoLISP programmers attempt deal with every conditional logic by the IF function. Of course, I could cite other examples of the same sorts of functional fallacies, but suffice to say, the best method to avoid such traps is to experiment, experiment, experiment, and read other people's code with an eye to learning something new.