The Apostrophe and the Quote Function
Contents
Introduction
In my experience, very little documentation exists describing the purpose of the apostrophe symbol in AutoLISP, and the explanations that I have encountered in existing tutorials are usually quite brief and almost bypass the main purpose of this operator.
Consequently, I have found that many novice AutoLISP programmers are often confused when faced with this symbol in existing code, and can also be unaware of when the apostrophe may and may not be used when writing code.
I have therefore put together an explanation that is hopefully comprehensible, detailing the purpose of both the apostrophe operator and the AutoLISP quote function, and the consequences of their use.
The Apostrophe
The apostrophe or single-quote character marks an expression or symbol as a literal expression, to be taken at 'face-value' and not to be evaluated by the AutoLISP interpreter.
The quoted expression may be any form of AutoLISP data, though the apostrophe is usually used with lists or symbols, since most other forms of AutoLISP data (strings, integers, reals) are already literal constants and hence do not require its use.
Constant Data
Expressions that contain constant or fixed data (that is, data that is unchanged when evaluated) may be quoted as literal expressions, as there are no symbols within the expression to be evaluated and consequently change the data content.
For example, consider the following list of integers:
_$ (list 1 2 3 4 5)
(1 2 3 4 5)
The above list is constructed by passing each integer as an argument to the list function. The list function will then evaluate each of the supplied arguments and will return a list containing all arguments in the order in which they were passed to the function.
However, since each integer is a constant, there is no need for the data to be evaluated by the list function, as the result will be unchanged.
Therefore, this list may quoted as a literal expression using the apostrophe to yield the same result:
_$ '(1 2 3 4 5)
(1 2 3 4 5)
Similarly, consider the operation of constructing a list of dotted pairs:
_$ (list (cons 1 "a") (cons 2 "b") (cons 3 "c"))
((1 . "a") (2 . "b") (3 . "c"))
Here, each integer and string pair are passed as arguments and evaluated by the cons function, which returns a dotted pair (since two atoms have been supplied); each of the resulting dotted pairs are then passed as arguments and evaluated by the list function to return the list as shown.
However, at each stage, the innermost data remains entirely unchanged, and consequently, every evaluation of the cons & list function is redundant and introduces inefficiency into the code.
The list of dotted pairs of constant data could equivalently be quoted as a literal expression using the apostrophe to yield the same result without the evaluation of three functions:
_$ '((1 . "a") (2 . "b") (3 . "c"))
((1 . "a") (2 . "b") (3 . "c"))
To offer a practical example, consider the list of dotted pairs which may be supplied to the ssget function as the filter list argument.
The following example will retrieve a selection set of all Closed LWPolyline objects in the drawing database:
(ssget "_X" '((0 . "LWPOLYLINE") (-4 . "&=") (70 . 1)))
Since the above filter list contains only constant data, the list may be quoted as a literal expression using the apostrophe.
More information on the arguments available for use with the ssget function and the filter list operators as used in this example can be found in my ssget Function Reference.
Variable Data
There are of course limitations on when the apostrophe may be used to quote AutoLISP data. One such limitation is when the data is not constant, but contains symbols or expressions which, when evaluated, will yield different resulting data.
Consider the following example:
_$ (setq x 5)
5
_$ (list 1 2 3 4 x)
(1 2 3 4 5)
Here, the symbol x is assigned an integer value of 5; this symbol and the integers 1 to 4 are then passed as arguments to the list function.
Since each supplied argument is evaluated by the list function, the symbol x is evaluated to yield the value 5, and the list is returned containing the evaluated value of the symbol, as shown above.
However, observe the result when the list is quoted using the apostrophe:
_$ (setq x 5)
5
_$ '(1 2 3 4 x)
(1 2 3 4 X)
Since the apostrophe marks the list as a literal expression, the content of the list is not evaluated by the AutoLISP interpreter, and the symbol x remains as a symbol in the list.
This demonstrates what is meant by the terms 'literal' & 'face-value'.
Expanding on our previous practical example, let's say that we wish to prompt the user to specify the name of a layer containing the Closed LWPolylines to be selected by the ssget expression:
(if (snvalid (setq lay (getstring t "\nSpecify layer: ")))
(ssget "_X" (list '(0 . "LWPOLYLINE") '(-4 . "&=") '(70 . 1) (cons 8 lay)))
)
Since the filter list now contains variable data as a result of the lay variable, the list can no longer be quoted as a literal expression, but must be constructed using the list function.
However, since three of the four dotted pairs contained in the filter list still contain constant data, these dotted pairs may be quoted as literal expressions using the apostrophe, with the final dotted pair constructed by passing the two evaluated arguments to the cons function.
Function Arguments
Note that in the above examples the quoted lists would almost always be supplied as arguments to other functions: whether that function is setq, to assign the data to a variable; list, when constructing a list containing lists of constant data (as per the last example); or perhaps ssget, as shown in the first selection example supplying the ssget function with a quoted filter list argument; to name a few.
However, in general, the quoted argument need not necessary be a list, as the following example demonstrates:
_$ (mapcar '+ '(1 2 3 4 5) '(6 7 8 9 10))
(7 9 11 13 15)
Here, the apostrophe is used to mark the + function as an argument for the mapcar function, with the two list arguments also quoted since they contain constant data.
Consider that if this function argument were not quoted, the function symbol would be evaluated by the mapcar function to yield the pointer to its function definition, and the mapcar function would consequently return a 'bad function' error.
The same logic applies when supplying an anonymous lambda function as the functional argument to be evaluated by mapcar on every item in the given list(s), as demonstrated by the following example calculating the midpoint between two points:
_$ (mapcar '(lambda ( a b ) (/ (+ a b) 2.0)) '(12.3 45.6 78.9) '(32.1 65.4 98.7))
(22.2 55.5 88.8)
For more information describing how the mapcar function operates, see my Mapcar & Lambda tutorial.
To offer another example, consider the following getvar expression:
_$ (getvar 'osmode)
255
Here, the symbol osmode is quoted to ensure that the symbol is not evaluated when passed as an argument to the getvar function. Since both the getvar & setvar functions accept either a string or symbol argument, this expression will then return the value of the OSMODE system variable, as demonstrated above.
Consider that if the symbol argument was not quoted, the getvar function would evaluate the argument to yield any value that has been assigned to the symbol in the active document namespace (e.g. via a setq or set expression); if the symbol holds no value (or indeed, holds a value other than a valid string or symbol argument), the getvar expression would consequently error with a 'bad argument type: (or stringp symbolp)' error.
Finally, observe the following expression which invokes the Visual LISP ActiveX getboundingbox method (for example, as used by my Bounding Box & Selection Set Bounding Box functions):
(if (setq ent (car (entsel)))
(vla-getboundingbox (vlax-ename->vla-object ent) 'pt1 'pt2)
)
The getboundingbox method requires three parameters: the VLA-Object whose bounding box is to be calculated, and two symbol parameters to hold the values output by the method.
The utilisation of output parameters enables the method to return multiple values, as opposed to the single value returned following function evaluation (which happens to be nil for this method).
When invoking the method, the symbol parameters are quoted to ensure that the symbol itself is supplied as an argument to the method, rather than the value obtained when such symbol is evaluated. Following successful evaluation of the method, the supplied symbols are assigned the lower-left & upper-right WCS coordinates describing the bounding box of the supplied VLA-Object.
The Quote Function
The AutoLISP quote function is the functional equivalent of the apostrophe symbol. This function accepts a single argument which may be any AutoLISP expression and simply returns the supplied expression without evaluating it.
As such, the quote function could be used in place of the apostrophe in our first example in the following way:
_$ (quote (1 2 3 4 5))
(1 2 3 4 5)
Here, the list of integers is passed as an argument to the quote function, and, instead of the first item in the list being evaluated as a function and resulting in a 'bad function' error (as would occur if the list was evaluated), the supplied argument is not evaluated, but simply returned by the quote function.
Similarly, the mapcar expressions from our previous examples could also be rewritten using the quote function in place of the apostrophe:
_$ (mapcar (quote +) (quote (1 2 3 4 5)) (quote (6 7 8 9 10)))
(7 9 11 13 15)
However, where the second mapcar example is concerned, the lambda function may be optimised when the code is compiled to a VLX or FAS file by substituting the function function in place of the apostrophe or quote function:
_$ (mapcar (function (lambda ( a b ) (/ (+ a b) 2.0))) (quote (12.3 45.6 78.9)) (quote (32.1 65.4 98.7)))
(22.2 55.5 88.8)
The function function is identical to the quote function with the exception that it instructs the Visual LISP compiler that the enclosed symbol or lambda expression is a function argument which may be linked & optimised during compilation.
Since lambda expressions are defined at run-time, the Visual LISP compiler cannot optimise these expressions during compilation, as is possible with built-in AutoLISP functions or named functions defined with defun. Similarly, when quoted using the apostrophe or quote function, the compiler treats the quoted lambda expression simply as literal data, without linking or optimising the function.
However, by using function, the compiler is instructed to optimise the lambda function during compilation to yield similar performance to built-in or named functions during evaluation.
The function function can also be used with symbols defining any built-in or named function, however, since these functions are already optimised when compiled, there is very little gain in performance, if any.
Run-time Evaluation
Since the quote function operates in an identical manner to the apostrophe, it may appear that this function is redundant since apparently the apostrophe could be used in its place with far fewer keystrokes.
However, there are situations in which the quote function must be used in place of the apostrophe to obtain the desired result.
One such example is literal expressions that are defined at run-time. (another is quines...)
Consider the following simplified example:
_$ (eval (list 'defun-q 'fun '( / lst ) (list 'setq 'lst (list 'quote (list 0.0 (* pi 0.5) pi (* pi 1.5))))))
FUN
When evaluated, the above expression will define the function fun which contains a single setq expression assigning a list of four numerical items (multiples of π) to the local variable lst.
I have intentionally defined the above function using a defun-q expression so that the result of the function definition may be revealed when the symbol fun is evaluated:
_$ fun
((/ LST) (SETQ LST (QUOTE (0.0 1.5708 3.14159 4.71239))))
Following definition of the function, observe that the setq expression is now assigning a literal list to the symbol lst, without repeated calculation of the list items, and more importantly, without any loss of precision of each item (the displayed values are truncated when displayed at the console for ease of viewing, however, these values are still stored to the maximum level of precision available).
Consider that in order to obtain the same result using a quoted literal list, each irrational multiple of π would need to be expressed in the code to around 15 decimal places to achieve the same precision.
Alternatively, the function could be defined such that the arithmetic expressions are included in the function definition:
_$ (defun-q fun ( / lst ) (setq lst (list 0.0 (* pi 0.5) pi (* pi 1.5))))
FUN
_$ fun
((/ LST) (SETQ LST (LIST 0.0 (* PI 0.5) PI (* PI 1.5))))
However, with the function defined in this way, the multiples of π are calculated and re-calculated unnecessarily every time the function is evaluated, introducing inefficiency. For the above example, this loss in performance is of course negligible and one must also consider the negative effect on the readability of the code when opting to define the function at run-time; however, if the list were to contain potentially thousands of entries, the repeated calculation would soon become noticeable.
Hence, this construct allows large literal lists which could be unmanageable to include directly in the program source code to be constructed at run-time whilst simultaneously retaining accuracy and improving efficiency.
To offer a concrete example of the quote function being used in this manner, consider my GrText function. The quoted vector list which appears at the top of this function definition is already quite large, but without the use of the run-time redefinition, the literal list would be twice as large!
Function References
Below are links to the formal documentation for the various functions described above.
As far as I am aware, there is no formal documentation for the apostrophe.
'Auto CAD > lisp' 카테고리의 다른 글
Error Message Troubleshooter (0) | 2021.02.20 |
---|---|
Building Association Lists: A Simple Block Counter (0) | 2021.02.20 |
Selection Set Processing (0) | 2021.02.20 |
Error Handling (0) | 2021.02.20 |
Prompting with a Default Option (0) | 2021.02.20 |