This is a practice that, in my experience, many new AutoLISP programmers overlook - sometimes resulting in elusive bugs in their programs.
This tutorial will give a brief overview of the defun function and moreover explain why localising variables is a good habit to get into. I have tried not to go into the small details in this tutorial to keep things clear.
Some Background on the 'Defun' Function
For a more complete overview on the defun function, read the help documentation provided in the Visual LISP Editor (VLIDE). Not sure how to access the help? See my tutorial on Retrieving Information about a Function.
The purpose of defun is clear from its name: define function. defun may be used to create a program that the user may call from the command line by typing the symbol used to name the function, or perhaps to create a subfunction which may be called from within another function to perform a particular task.
The defun function is used in the following way:
(defun <symbol> ([arguments] [/ variables]) <expressions>)
- symbol is the name of the function (if prefixed with 'c:' the function may be called from the command line).
- arguments are symbols representing data that the function requires when called (more about these some other time).
- variables are the symbols used to hold data within the function.
- expressions are all the statements that make up the function itself.
(defun test ( arg1 arg2 / var1 var2 ) ;; Function taking two arguments, with two local variables ;; Expressions (setq var1 (* arg1 2.0) var2 (+ arg2 3)) (print (- var2 var1)) (princ) ) ;; End of defun
(defun c:MyCommand ( / a b c ) ;; Function with three local variables (setq a 1.0 b 2 c 3) (print (- b (/ a c))) (princ) )
(defun myfunc ( x y ) ... ) ;; Function takes two arguments (defun myfunc ( / a b ) ... ) ;; Function has two local variables (defun myfunc ( x / temp ) ... ) ;; One argument, one local variable (defun myfunc ( ) ... ) ;; No arguments or local variables
Why Localise the Variables?
If a symbol is included within the 'variables' argument of the defun statement, when calling the function, memory space is made available to hold any data later bound to that symbol, and furthermore the data is disassociated with the symbol after the function has completed; hence the symbol will now point to whatever data it held before the function was called, or may be nil.
All else aside, this is certainly a 'cleaner' method, as, any variables set within the function are 'cleaned up' afterwards, and hence cannot interfere with other programs using the same symbols for variable names, or indeed waste space in the memory.
What are the Consequences?
Usually, programmers will see no difference in the behaviour of a function with variables localised or not - but there are a few cases in which one can receive unexpected results, and such bugs are sometimes hard to spot.
Take this example (Notice no variables are localised):
(defun c:test ( ) (foreach x '(1 2 3 4 5) (setq lst (cons x lst)) ) (print lst) (princ) )
Upon running the above code, the following will be displayed at the command line (be sure you know why it is reversed):
(5 4 3 2 1)
Now, in the same AutoCAD session, if we run the code again, we receive:
(5 4 3 2 1 5 4 3 2 1)
The list has doubled! This is because the variable lst still holds the list data even after the function has completed. Hence, when evaluated for the second time, new values are added the existing list.
This certainly highlights one major issue, but what if we had another function, also without localised variables, using the same symbol for a variable name?
(defun c:test ( ) (foreach x '("1" "2" "3" "4" "5") (setq lst (cons x lst)) ) (print lst) (princ) )
Upon running the above code, we now have a list containing a mixture of data types - this is getting really messy!
("5" "4" "3" "2" "1" 5 4 3 2 1 5 4 3 2 1)
To avoid these issues, we localise the variables:
(defun c:test ( / lst ) (foreach x '(1 2 3 4 5) (setq lst (cons x lst)) ) (print lst) (princ) )
Now we can be confident that everytime we run the program, the value of lst will be consistent everytime:
(5 4 3 2 1)