Chapter 3. The Visual LISP IDE
The Visual LISP Integrated Development Environment (VLISP IDE) includes
tools that assist in writing, modifying and debugging the source code.
Now we can, once we have coded the program using the Programming Editor,
check its operation step by step, viewing at all times the output
returned by the evaluation of each one of the expressions and the value
the variables are assuming without having to include any additional
expressions in the program. It also provides the means to package and
distribute applications in a single executable file that contains the
DCL dialog interface. These operations are performed within a single
environment, allowing text editing and program debugging, plus
interaction with AutoCAD and other applications.
To start programming
in Visual LISP two are the tools we must master: the Console and
Editor. The simultaneous use of both, Console and Editor, is one of the
keys to success in Visual LISP programming.
- Working in the Visual LISP Console we can understand why we say that LISP is an interactive language. If we type an expression in the Console, we'll obtain its value immediately. This way we can check the expressions that afterwards we will include in our programs.
- It is in the Editor where we establish the final form of our programs, saving them to the data storage units of our system.
This Chapter includes the following sections:
3.1. The Visual LISP IDE user interface.
3.2. Interactivity: The Visual LISP console.
3.3. The Programming Editor.
3.4. Interaction between the Editor and the Console.
3.5. Summary.
Chapter 4. Evaluating expressions
The Console is the tool, along with the Editor, that we shall use the
most in the development of our programs. It is also the ideal means for
learning LISP. Often we will try out an expression directly in the
console taking advantage of the possibility to repeat again and again
that evaluation making each time the necessary changes, either to the
syntax of the expression or to its arguments. Once we find the form that
achieves the intended goal, we can simply copy it to the Editor window
in the position it will occupy in the finished program.
Evaluating expressions in the Visual LISP Console. |
In this and
the following chapters we will use the term LISP to refer to those
concepts, procedures and functions that match the specifications of the
ANSI Common LISP standard. In the case of those that are specific to
AutoCAD we will use the term AutoLISP when it refers to issues already
present in release 14 or lower, and Visual LISP to those introduced in
this programming environment since release 2000.
If we follow the exercises in this chapter we will have verify the following aspects:- LISP programs are composed of expressions (also called forms). An expression can be an atom or a list whose first term is a function followed by zero or more arguments. Prefix notation used in LISP syntax allows a function to receive any number of arguments.
- LISP evaluates expressions according to the following rule: all arguments are evaluated from left to right and the results are passed to the function that appears as the first term of the list. An exception to this rule is the special form quote, Which returns its argument without evaluating it.
- Two data types are characteristic of LISP: symbols and lists. Lisp programs are expressed in the form of lists.
- Symbols can represent LISP objects of any kind. The variables used in a program are symbols representing the data on which the program operates. In LISP the values have type, not the variables.
- LISP functions always return a value and sometimes produce collateral effects. They can be used in a program either for the value returned, for its collateral effects or for both.
- We say that LISP functions are non-destructive in the sense that they do not change the value of the variables that are passed to them as arguments. To change the value assigned to a variable it will be necessary to assign it expressly the new value using setq. The new value is returned by setq that also assigns it to a symbol as its collateral effect.
- The basic functions for manipulating lists are cons, which builds them, car that returns its first term and cdr that returns the rest of the list.
- We must distinguish proper lists from dotted pairs. The dotted pair is a data structure composed of two parts, the car and the cdr. The lists are actually chains of dotted pairs in which the last cdr points to nil.
- There are functions like apply and mapcar designed specifically for the processing of data structured as lists.
- A function can be applied to all the terms of a list by apply.
- The mapcar function applies a function taking in succession as arguments each item of one or more lists.
- The lambda symbol allows designing anonymous functions according to the user's requirements. Lambda expressions can be used in a manner identical to the language primitives and are often used in combination with apply or mapcar.
This Chapter includes the following sections:
4.1. Data.
4.2. Expressions.
4.3. Symbols and assignment.
4.4. Lists.
4.5. Variables and data types.
4.6. Manipulating the elements of a list.
4.7. Lambda.
4.8. Summary
Chapter 5. User-defined functions
Many times when writing a program it would be practical to have a custom
function that receiving certain arguments would return a certain value.
This can be easily done with LISP. The user can define functions
designed specifically for the type of applications he is developing. The
technique of building a program from small user functions aimed at
solving specific problems is known as bottom-up programming. Over the
time the user will have created his own library of functions that will
bring the programming language closer to the type of applications that
he usually writes. The functions that we propose in this book are mostly
conceived with this purpose.
This Chapter covers:- Defining, loading and executing user functions.
- The use of global and local variables.
- Predicates and Conditionals.
- Control Structures:
- Recursion.
- Iteration (including repeat, foreach, while and mapcar.
Recursion is a very effective control structure. It is said that a
function is recursive when it calls itself. To design a recursive
function it is necessary to:
- Determine when it should stop.
- Decide on the action to take.
- Seek ways for subdividing the problem as such action plus the rest.
Because of its nature, recursion is particularly suited to list
processing, especially if used as a representation of hierarchical tree
structures implemented from varying nesting levels, unknown beforehand.
Recursive procedures are limited by available memory, as function calls
remain on hold until the other calls are completed.
Tracing of a recursive function. |
Operators specially designed to implement repetitive loops without the use of recursive procedures are available. These are the iterative functions.The simplest forms of iteration are repeat and foreach.
But while is
more effective in that it allows to establish test conditions for
ending the loop. These conditions may take into account various
parameters being possible to group several of them by expressions like and and or.
Loops implemented with repeat, foreach and while may, like other LISP expressions, be nested inside each other. This way implementing highly complex processes is possible.
This Chapter includes the following sections:
5.1. Defun.
5.2. Loading and executing user functions.
5.3. Global and local variables.
5.4. Predicates and Conditionals.
5.5. Recursion.
5.6. Iteration.
5.7. Summary
Chapter 5 source code.
Your questions or comments about these Chapters' contents are welcome!
A reader asked me a couple of days ago:
ReplyDeleteI’m completely stumped by the last function on Page 74.
(defun sort-list (lst func)
(mapcar '(lambda (x) (nth x lst))
(vl-sort-i lst func)))
;;;Listing 5.8. List sorting function.
What I DON’T understand, is how
'(lambda (a b) ( < (car a) (car b)))
is used as the “func” argument in sort-list, and how it is applied to the underlying (vl-sort-i lst func) part of the defun.
Your question sums up to one issue: How do the AutoLISP sorting functions work?
ANSWER:
Vl-sort and vl-sort-i operate as what is known as a Comparison Sort. This means that it reads two list elements comparing them through the comparison operator supplied to find which of the two elements should occur first in the final sorted list. There are many algorithms that may be used to do this, but the one used internally is not documented.
For more information on the subject of Comparison Sorting algorithms you can see http://en.wikipedia.org/wiki/Comparison_sort.
But let’s see how vl-sort and vl-sort-i work. As they use a Comparison Sort algorithm they operate taking a pair of values and applying to them the comparison operator. This means that they operate repeatedly by comparing two of the list’s terms each time and performing some kind of reordering that depends on the sorting algorithm implemented.
You said that:
“What I DON’T understand, is how ‘(lambda (a b) ( < (car a) (car b))) is used as the “func” argument in sort-list, and how it is applied to the underlying (vl-sort-i lst func) part of the defun.”
The reason this can be done is precisely that vl-sort and vl-sort-i take each time a pair of arguments from the list. These arguments are the ones that the lambda function identifies with the symbols a and b.
Let’s assign a list of points to a symbol, let’s say lst. As we are only going to compare the first term of each sublist, the other values are of no interest, so we will use just zeros. Could be any number:
(setq lst '((9 0 0)(2 0 0)(6 0 0)(4 0 0)(7 0 0)))
To check which numbers are compared each time by the sorting functions we will modify the the lambda expression so it will also print to the console the values received as a and b. This will be done by nesting the first car in a print expression and the second car in a princ expression. This way print will introduce a new-line before a’s value and a space after it. Then princ will output b’s value printed in the same line. This way the values compared in each cycle will be printed in a different line.
_$ (vl-sort lst '(lambda (a b) ( < (print (car a))(princ (car b)))))
2 9
7 4
4 6
7 6
4 2
4 9
6 9
7 9((2 0 0) (4 0 0) (6 0 0) (7 0 0) (9 0 0))
As we can see, vl-sort checks the following relations to sort the list:
If 2 is less than 9,
If 7 is less than 4,
If 4 is less than 6,
If 7 is less than 6,
If 4 is less than 2,
If 4 is less than 9,
If 6 is less than 9,
If 7 is less than 9.
After the last pair of numbers vl-sort returns the sorted list, in this case the sorted point list.
If we do the same using vl-sort-i the only difference is that the list returned will not include the point sublists, but their indices. This is why this returned list is processed to recover, using the nth function, the sublists identified by these indices. Why is this done? It is documented that in certain cases vl-sort will delete repeated entries. So this way we can guarantee that no item will be lost in the sorting.
Vl-sort and vl-sort-i are welcome additions to AutoLISP. Before Visual LISP we would have to program our own sorting functions. As Reini Urban said “Generally speaking, sorting in plain AutoLISP is a pain in the ass”. http://autocad.xarch.at/lisp/#sort