AutoLISP Lesson 5 - VARIABLES
Variables are used as the principle means to store values. Files may also be used to store values, but variables are what store values for short-term use.
AutoLISP variables may be any of (4) four "types"; integers, reals, points, or strings. Variables in AutoLISP are automatically "typed" depending on the value they contain, so no "typing" of variables is required.
The function SETQ will be used to create a variable. A variable will contain a nil value until some value is bound to it. If you reference a variable before you have assigned a value to it, you will receive nil as the result of the reference.
(setq a 3) ;Integer variable (setq a 3.00) ;Real variable (setq a (list 1.50 2.375 1.00) ;Point variable (setq a "This is a string.") ;String variable
When we discussed comments in lesson 3, we neglected to touch on the easiest method of commenting of all; variable names. If you use proper variable names, much of your code can be self-documenting. You need only add comments to code, which is not clear simply by reading it.
Variables can be named anything you want providing the first character is alphabetic, and you don't use the reserved characters from lesson 2.
Many AutoLISP programmers try to keep variable names to (6) six characters or less. This is due to the way variables are stored in AutoLISP. If a variable name is less than 6 characters AutoLISP only requires (2) two nodes to store the value. If you make the variable name longer than (6) six characters, AutoLISP stores the value in one node and stores a pointer to the name in the other node. The name itself is stored in memory taken from the heap for string storage. Heap space is limited and that is why some programmers try to keep variable names short.
Two things have happened that makes this rule not so carved in stone as it used to be. The advent of virtual memory and learning how to program cleanly has done much to solve the problem of running out of memory. I can't do much about the amount of memory in your computer or the amount of memory AutoCAD needs, but I can show you how not to run out of memory by programming in a clean manner.
Naming a variable is not like naming the family pet, you can't choose a name cause it's cute or it's what the variable looks like. You need to choose names based on what it is the variable holds. You must look at the variable and its name as being the same thing. If you can't tell what the variable is, maybe you don't need the variable anyway.
If a variable holds the point for a line, state that in the variable name. First tell which line, and then tell which point. Variables can be named well or they can be named badly, it's all up to you.
The bad variable names have been made slightly more palatable by the use of comments to say what they are, but the good variable names are much more descriptive and require no comments. Anyone who has programmed for awhile knows comments rarely come easy, sometimes not at all. It is better to give your variables names, which do not need to be explained, so a missing comment later will not lead to confusion about what the variable is holding.
Also note that end-line comments no matter how well done, clutter up code and make it harder to read. If you can avoid using end-line comments, you will be much better off.
Some times you have many variables that seem to hold the same type values. They may seem the same but there is always something different about them that the name can tell you.
I have seen programmers go as far as giving many pages of comments to explain what each of their variables was supposed to hold. Having a decoder ring is no substitute for proper variable naming. If you give full variable names you will have less commenting and a easier time when hunting problems.
Indexes through a loop are one of the areas where it is sometimes difficult to come up with a name. Many programmers have a name they give to all indexes, such as Index, Idx, Ndx, or some such thing that they are comfortable looking at. They know when they read their code what these things are, but others may not. If you are going to use short names for indexes be sure to tell what it is that you are indexing through.
An index variable is like any other variable and needs a proper name to be truly understood.
Status variables should be given names better than "flag". Everyone seems to use flag for status variables. The first thing we have to do to give truly meaningful names to variables, is get away from thinking about our problems from the viewpoint of the computer. A flag tells if something is right or wrong, it should be given a name that reflects the item we are checking.
Status variables are not used extensively in AutoLISP programming, but if you use them, give them real names.
Variables should be named in a manner that also identifies their scope and / or type.
should be identifiable as such by reading their name. Some people use prefix or suffix notations to accomplish this. An example would be to add a g or g_to the beginning of the variable name:
This identifies the variable as a list that is available to all the functions in your program. I am not a fan of putting the "_" character in variable names as a separator. I prefer the naming convention where you capitalize the first letter of each word or abbreviation in the variable name.
Are normally capitalized as a way of identifying them. They stand out from the rest of your code so you know they are a constant.
Are parameters to a function, should not be modified inside the function. It is recommended that you identify input variables so if you see one getting modified you can do something else. It will be easier to read your code and tell which variables are parameters if you identify them in the name. An "i" or "ip" may be prefixed to the name to identify them.
A CONSTANT in AutoLISP is really nothing more than a variable you have set to a certain value, and know that you shouldn't change to any other value. It's a conceptual, idea rather than a "type" as in other languages. The variable PI has been preset to contain the value of pi. The concept of CONSTANTS is a very powerful idea that you should become familiar with, and try to make use of.
A constant should be used in place of magic numbers in your programs. A magic number is a numeric literal such as the value of PI, or a conversion factor that will not change throughout your program. If you use numbers that have no explanation other than the number itself, you and anyone who follows you, will have trouble figuring out what the number is for when it's time to change the program.
Many times we will use constants to identify boundary conditions our program should not go above or below. In these cases a "_MAX" may be appended to the name to tell which end of the boundary we have hit.
(setq LIST_MAX 10) (if (/= LstNdx LIST_MAX) (do_this))
The preceding code sets our maximum list number. It then says if we haven't reached that number to go ahead and do whatever it is we're up to. If I had wrote it using the 10 as the check value, we would have very little indication as to what I was checking for. Also if I used LIST_MAX somewhere else and I decide I need the maximum lists to be 12, I only have to change it in one place instead of looking all through my code to find everyplace I may have put 10.
If you use a constant in only one place, it is nice to know what the constant is referring to. If you use a constant in more than one place, it is nice to be able to change it in only one place and have it replaced everywhere else.
Acronyms should never be used to name a variable. It would be very difficult to tell MFICO means "Maximum Files I Can Open".
Variables have what is referred to as scope. The scope of a variable is directly related to the amount of other code that has access to the variable. Generally the smaller the scope of your variables the better off you are. That is why global variables are frowned upon.
If a variable has a large scope, many functions in your program can directly access them and in doing so change them. Variables should only be changed by functions that are intimate with the variable. A variable should be created for only one function or module, and that function or module should be the only one that has direct access to the variable.
If you list your variables in the local variables section of the parameters list, you limit the scope of the variable to that function and any function defined within that function.
(defun my_prog ( / Var1 Var2) (setq Var1 Var2) )
Var1 & Var2 are now local to only the my_prog function. If I had defined another function within my_prog the variables would have been accessible to it also.
(defun my_prog ( / Var1 Var2) (setq Var3 (sub_prog)) (defun sub_prog ( / ) (* Var1 Var2) ) )
As you can see by the example Var1 and Var2 are accessible to sub_prog, because sub_prog was defined within my_prog. This is the basis for creating modules and will be discussed in later lessons. For now all you have to remember is if a variable is listed in the parameter list, its scope is confined to the function where it's listed.
Each variable should be initialized prior to using it. At some point you could end up with two variables with the same name, and by initializing the variable prior to use, you prevent yourself from using the value it may have picked up in a prior life.
This type of mess should never happen, but sometimes due to time constraints or some other reason we just get into too big a hurry and things can go wrong. The best way to save yourself grief is to use a little defensive programming.
Each variable you create should be used for one purpose and one purpose only. The most common variable to be used for two purposes is the index variable. If you are using the same index variable in two or more different places, you have not named your variables or your functions correctly. An index variable should have the function it's a part of, as part of its name. This will prevent you from indexing into a data structure at the wrong point because you have forgotten to initialize a variable before using it.
Another thing to keep in mind when thinking about the purpose of a variable is to make sure you use all variables that you declare. Sometimes in the development of a program you change the way a function works, and declare new variables to match the way the modified function works. Be sure to clean up any old variables you may have set for the prior version.
It would be easy to say global variables should never be used, but this is not the case. There are times when the only way to get something done is with a global variable. Fortunately this is rarely the case.
Global variables have many problems associated with their usage. No one of these problems are any worse than the next, they are all bad and should be avoided.
If you have failed to initialize your data prior to usage, a global variable will return you the last value that it held. This means global indexes will be wrong, global values of length or position will be wrong, and any other value you make global will probably also be wrong. Limit the scope of your variables.
A global variable can be changed by any function in your program. Only the current drawing session limits its scope. This means other programs you may load could also pick up the global variable and use its value instead of initializing the variable to its local "in context" value. Don't let one program walk over another program. Probably the best reason I have for keeping variables local to a function is code reuse. If I keep my variables local to a function or module in my program, I can pull that function or module out and us it in other programs. This code reuse is where you get greater productivity in your programming. Don't discount code reuse in any manner.
At some point you will find global variables to be a necessary evil and will be forced to use them. One of the big reasons to use global variables is to pass default values to your programs. If you are coding to make your program look and feel like built in AutoCAD commands, you will need to pass default values to your program. Global variables or an external file are good ways to save values from one run of your program to the next.
There is another way to do this and avoid global data all together. AutoCAD has been supplying user system variables for you to use for several releases now, and this might be the best solution to the global variable problem. This is not however a fail proof system, as other programs may also be using the user system variables and write over your default values.
If you must use global variables there are several things you can do to protect yourself from the problems. Using the following methods should reduce the risk that something will get walked on by another program. Start writing your program with all variables local, and only make variables global as necessary. If you must increase the scope of a variable, try increasing its scope to the module level, before you make it global. In this way you can keep the number of global variables to a minimum.
Remember to make global variables obvious. Naming them in a manner that you will know by looking at them is the best way to keep global’s straight. You may even consider listing global variables in the note comments at the header portion of your program. If global’s are listed someone doing maintenance to your program will be able to tell which variables are which.
Access routines can be written to gain access to local variables from other function. If other function need access to local variables, try writing access routines before you just make the variables global. An access routine can control the access to the variable to any level and keep the variable from being changed. You can build a "read only" access level into the routines to protect your variables from outside modification. Throwing all your data into a big structure and accessing the data there is not protecting your data or making it easier to read or understand your program. When writing access routines the tendency is to write one big structure to hold everything, that way you only need one access routine to get to all the data. Data should be kept local to the modules using it, and multiple access routines should be written to get to the data in any module.
Other variable considerations
I mentioned the problems with "magic" numbers before, but I'm going to restate what I already said. Magic numbers should be avoided no matter what. If you have numbers that are constant, use a constant variable so that their meaning is obvious. Magic numbers cause more problems when maintaining programs than they’re worth.
Type conversions should be made obvious. AutoLISP will automatically convert an integer value to a float if you are doing math with an integer and a float. AutoLISP will also allow you to assign any data "type" to a variable name. The combination of these two things can lead to problems if you assign an integer to a variable you thought was a float.
If you divide an integer by a float you get a float. If you divide and integer by an integer you get an integer. This means that if you assign an integer to a variable you are treating like a float, the returned value will be an integer, which will be the wrong value.
When assigning floating point numbers to variables always use trailing 0's. The trailing 0 assures a floating-point number and is easier to read when your trying to figure out what your program is up to. The FIX function will convert a float to an integer and the FLOAT function will convert an integer to a float.
If you have a string that is used many times throughout your program, it could save typing and some memory space if you placed the string in a variable, and called the variable instead of typing out the whole string. You can use these variables for your program name and error messages among other things. This lets you change your program name or modify error messages in one place, and it will be reflected throughout your program.
I guess that's enough for you to absorb for now. As you can see variables are extremely important and you have to think about what you are up to all the time. Don't use variable s indiscriminately, and don't make them all global just to keep you from having to think too hard. It's worth the extra effort to use them correctly.
Featured blocks of the month