Programming Course

Chapter 3

Functional Programming

Lisp II - Basic Functions

3.1 Evaluation

The basic unit of evaluation in LISP is an expression. An expression is a sentence in Lisp and it can be a value, a symbol, or a list. It is the basic unit which the LISP interpreter evaluates in one step.

Every LISP expression that you enter from the keyboard is evaluated, and the result of the evaluation process is returned. The AutoLisp environment also has a LISP interpreter - sometimes called an evaluator, and it works as follows:

Although this process may look complicated, it is possible to define an interpreter for the LISP language in LISP itself!

3.2 List Operations

3.2.1 car, cdr

A list may be made up of zero of more atoms, for example: (3 2.6 -8.9). If we want to access or change any of the atoms in the list, two LISP operations are provided.

(car a_list) This operation returns the first element of an argument list.

(cdr a_list) This operation returns a list of everything in an argument list except its first element.

Examples: Evaluate the following expressions exactly as shown, including the quote (') character before the lists.

(car '(a b c)) => A

(cdr '(a b c)) => (B C)

(car '((3 b) c)) =>

(cdr '((3 b) c)) =>

(cdr '(a)) =>

What happens if you evaluate the following expressions?

(setq a_list '((23.9 ab)(apples oranges cherries)(dogs cats cows)))
=>

(car (car a_list)) =>

(cdr (cdr a_list)) =>

(car (cdr a_list))=>

(cdr (car a_list))=>

When a number of list operations involving => and cdr are needed, it is possible to combine them into compound operations by joining a (from car) and d (from cdr) between the letters c and r. For example, the previous four examples could be rewritten as follows:

(caar a_list) =>

(cddr a_list) =>

(cadr a_list) =>

(cdar a_list) =>

3.2.2 cons, append, list

In order to insert new symbols into a list, the following operations are available in LISP.

(list a_sym b_sym ...)
Returns a new list formed by putting all the arguments into one big list.

(list 'a 'b) => (A B)

(cons new_sym a_list)
Returns a new list formed by inserting the first argument at the head of the second argument list. The first argument can be an atom or a list.

(cons 'a '(b c)) => (A B C)

(append a_list b_list)
Returns a new list formed by inserting all arguments together into a new list. Arguments need to be lists.

(append '(a b) '(c d)) => (A B C D)

Examples: Enter the following expressions exactly as shown.

(not (< 0 1.5)) => nil

(not (= 2.5 3.5)) => T

(and (< 2 3 5) (/= 0 1)) =>

(and (< 3 2 4) (/ 1 1.9)) =>

(or (> 2 3 4) (/= 3.5 6.5)) =>

(or (> 2 3 4) (= "ab" "AB")) =>

(list 'a '(b c) 3 '(a 3.9)) =>

(setq f1 '(a b c)) =>

(setq f2 '(d e f)) =>

(cons 3.5 f1) =>

!f1 =>

(append f1 f2) =>

!f1 =>

!f2 =>

(setq f1 (cons 3.5 f1)) =>

!f1 =>

(setq f2 (append f1 f2)) =>

!f2 =>

!f1 =>

3.3 Other Functions and Special Forms

LISP distinguishes between functions and special forms. Normally, when a LISP expression is evaluated by the LISP interpreter, every component of this expression, whether atom or list, is evaluated as part of the evaluation process. Special forms differ from this rule in that one or more components of the expression are not evaluated.

3.3.1 Delay Evaluation

LISP expressions are always immediately evaluated by the LISP interpreter, unless the evaluation of the expression is explicitly blocked by the user. quote is a predefined LISP function that delays evaluation.

(quote expr) quote is a special form, it is an evaluation blocker and returns expr without any evaluation.

´expr A quote character followed by expr is an abbreviation of (quote expr).

Unless a list is preceded by a quote ('), the LISP interpreter will attempt to evaluate the list assuming the first argument is or defines a function. Examples:

(setq a (+ 2 3)) => 5

(setq a '(+ 2 3)) =>

(setq b 'a) =>

3.3.2 Force Evaluation

Sometimes it may be necessary to force the LISP interpreter to evaluate an expression, for example if evaluation of the expression was blocked earlier with a quote.

(eval expr) eval is a function, it applies an extra evaluation to expr and returns the result.

(eval 2) => 2

(eval ´(+ 1 2)) =>

(setq a 2) => 2

(eval ´a) =>

(eval (eval ´a)) =>

(eval ´(+ a 4)) =>

(eval (list ´+ ´a 4)) =>

3.3.3 Variable Assignment

(setq symbol expr)
setq is a special form. The first argument should be a symbol which will not be evaluated and is assigned with the value of the second argument.

(set expr1 expr2)
set is a function. The value of the second argument is assigned to the result of the evaluation of the first argument.

(setq a ´b) => B

(set a 3) =>

!a =>

!b =>

3.3.4 apply

(apply func list)
apply is a function. It applies a function to the list and returns the result of the application.

(apply ´+ ´(1 2 3 4)) => 10

(apply ´* ´(1 2 3 4)) =>

(apply ´max ´(2 4 3 8)) =>

(defun average (alist)
(/ (apply ´+ alist) (length alist))
)
=> AVERAGE

(average ´(2 4 6 8)) =>

3.4 Self-defined Functions

All the examples introduced so far use predefined operations in LISP, e.g., car, cdr, +, *, minusp, zerop, etc. These are defined as functions made up of nothing but LISP expressions. LISP also allows you to define your own functions.

3.4.1 defun

To define your own functions in LISP which you can reuse again and again, the following form should be used.

(defun function_name arguments
expression_1
expression_2
...
)

function_name is written in italics to indicate that any symbol (usually a name suggesting what a function does) can be used, arguments can be any list of symbols, i.e., the argument names, even an empty list. The rest of the form are one or more valid LISP expressions, including AutoCAD commands. The meaning of the arguments is explained below.

Example: For the following example, you should open a Jot window and enter the following text. Save the file as test.lsp and then load it into AutoCAD by entering at the command prompt: (load "test")

(defun test_function ()
(setq a 2.0)
(setq b 3.0)
(setq c '(This is a list))
(princ "Square of variable a: ")
(princ (* a a))
(princ "\n")
(princ "Cube of variable b: ")
(princ (* b b b))
(princ "\n")
(princ "Value of variable c: ")
(princ c)
(princ "\n")
(setq d "this is a string!")
(princ d)
(princ)
)

The empty list after the function name test_function means that this function has no arguments. If the file is successfully loaded, the LISP interpreter will return the atom test_function. Now you can use it just like any other predefined LISP operation. Since this function takes no arguments, simply enter the function name to evaluate it:

(test_function)

The usefulness of a function without any arguments is rather limited, as it will give the exact same result every time it is evaluated. The following function square illustrates the use of arguments in functions. It takes a single argument x and returns the square of the value of x.

(defun square (x)
(* x x)
)

(square 4) => 16

3.4.2 lambda

What is a lambda expression?

A lambda expression has a list of parameters, and a body which consists of a sequence of expressions. The list of parameters defines the environment in which the expressions are to be evaluated. Lambda calculus, introduced by Alonzo Church in 1941, led to the development of LISP and functional programming. A lambda expression, explained below, is similar to the defun construct above. It is useful when you need to use a procedure in only one or a very few places in a program and you do not want to define a special procedure by a unique name.

(lambda args expr....)
lambda
is a special form. None of its arguments are evaluated. It returns a lambda expression which can be applied to a number of arguments.

(defun name args expr...)
defun
is a special form. None of its arguments are evaluated. It assigns a lambda expression to its first argument.

(defun a (x)(+ x x)) is equivalent to (setq a (lambda (x)(+ x x))) in AutoLisp.

3.5 Exercise 3 - Functions

This exercise gives you an opportunity to define your own functions. Each of the required functions draws some elements on the screen. You have to figure out how and where coordinate values will need to be changed so that each function generates elements in different parts of the screen (i.e. your drawing). The following three LISP functions are required:

  1. Create a function draw_pline with arguments width, height and color. The function draws a closed polyline specifying width as width and color as color. Next, it changes the thickness of the element to height. Then, it generates a polar array of the element, forming a complete circle.
  2. Create a function draw_solid with arguments width, height and color. The function does the same thing as the function draw_pline except that the command solid should be used instead of pline to draw the basic element. Note that solid requires a slightly different order for the specification of the vertices.
  3. Create a function draw_box with the same arguments. This function also creates a circular polar array of similar elements. For this function use the box command. Note that this command generates a cube, i.e. you do not have to draw a square and then change its thickness as in pline or solid commands. After you generate an array of the cube, use the union command to join the solids together into a single composite solid.
  4. Add one more function to your file that executes each of the above functions. Do not forget to specify widths, heights and colors. Also set the viewpoint and shade the drawing within this function.
  5. Create a snapshot of the final result and submit both the snapshot and the LISP file containing all four functions.

Besides realizing that AutoCAD commands are valid LISP expressions that can be used as part of functions, you will also see what the differences are between polylines, solid faces, and real solid volumes!