<<< Kernel Interface Driver (KID) - an Overview | Chapters | Object-Oriented KID >>> |
As previously discussed in the opening chapter we have assumed that users of KID have knowledge of LISP, but for those who are a little rusty or unsure of the concepts used, the following section is designed to get you started.
You should think of LISP as an interpreter. It evaluates or attempts to evaluate messages which you pass to it. The messages which you pass are called symbolic expressions (s-expressions).
In the following examples of s-expressions notice that numbers evaluate to themselves:
> ( times 3 4 ) 12 > 3.1417 3.1417 > ( times 3 ( plus 2 2 ) ) 12 |
S-expressions are composed of lists and atoms.
Atoms are entities which LISP treats as whole items, i.e. they cannot be broken down further. Examples are:
Lists are chains of elements bounded by parentheses, where elements are either atoms or lists themselves. For example:
(3 4 5 ) (a d f ) ( plus 2 ( times 4 3 ) ) ( ) --- empty list |
When evaluating lists LISP applies the following criteria:
S-expressions which are preceded by a quote ( ' ) are NOT evaluated, for example:
> ´ ( 3 4 ) ( 3 4 ) > ( quote ( 3 4 ) ) --- is equivalent to the above ( 3 4 ) > ´a a |
As previously discussed, strings can be used as variables. They can be bound to values using the
setq
operator, for example:
> ( setq a 3 ) 3 > a --- `a´ now evaluates to 3 3 > ( setq a ( add1 a ) ) 4 > a 4 |
A side effect of these type of operations is that the complete s-expression always evaluates to a result.
The following examples are equivalent forms:
> ( setq a 2 ) > ( set ´a 2 ) > ( set ( quote a ) 2 ) |
A number of symbol-strings are predefined by the system, for example:
plus
,
times
,
add1
(operator names)
nil
( the empty list, or logical false )
t
( the logical true )
Symbolic operations on lists consist primarily of taking lists apart and building them up. LISP provides two basic functions for taking lists apart, these are
car
and
cdr
. Both are functions of one argument, which should be a list, and they always cause their argument to be evaluated.
car
returns the first element of this list, for example:
> (car ´(a b c)) a |
cdr
returns the list with its first element missing, for example:
> (cdr ´(a b c)) (b c) |
car
and
cdr
are considered non-destructive as they do not actually change the lists on which they operate, for example:
> ( setq x ´(a b c)) (a b c) > x (a b c) > (car x) a > x (a b c) > (cdr x) (b c) > x (a b c) |
car
and
cdr
can be embedded in a single call, for example:
> (cdr (car ´((a b c) (d e f))) (b c) > (car (cdr ´((a b c) (d e f))) (d e f) > (car (cdr (car (cdr ´((a b c) (d e f)))))) e |
Code containing long strings of cars and cdrs is hard to follow. Alternatively, the same calls can be made by the single function that corresponds to the sequence of calls used. For example, the previous examples would use these single calls to achieve the same results:
> (cdar ´((a b c) (d e f))) (b c) > (cadr ´((a b c) (d e f))) (d e f) > (cadadr ´((a b c) (d e f))) e |
The function
element
is a shorthand for embedded
car
and
cdr
calls, for example, in the following example
element
returns the third element of the given list:
> ( setq x ´(1 4 6 7 ) ) (1 4 6 7) > ( element 3 x ) 6 |
Just as
car
and
cdr
take lists apart,
cons
builds lists up.
cons
is a function of two arguments where the second argument should always evaluate to a list.
cons
evaluates both of its arguments, and then returns as its value the list obtained by taking the second argument and placing the first one in front of it, for example:
> (cons ´a ´(b c)) (a b c) |
cons
can be considered to be the inverse function of
car
and
cdr
, as
cons
always produces a list whose
car
is the first argument to
cons
, and whose
cdr
is the second argument.
Like
car
and
cdr
,
cons
is non-destructive.
If the second argument to
cons
is an atom then the result is a dotted pair rather than a list. In most cases this is not a desirable result and the use of
list
would produce preferable results. However, the syntax for the input of PK option structures requires the use of dotted pairs for which
cons
should be used. For example:
(cons ´a ´b) -> (a . b) (cons ´a ´(b c)) -> (a b c) (cons ´(a b) ´c) -> ((a b) . c) (list ´(a b) ´c) -> ((a b) c) |
cons
can be used to build up complicated s-expressions, for example, to create the lists (1 2 3) and (a (b c) d), using
cons
we would:
> (cons ´1 (cons ´2 (cons ´3 nil))) (1 2 3) > (cons ´a (cons (cons ´b (cons ´c nil)) (cons ´d nil))) (a (b c) d) |
As this is obviously cumbersome, the
list
and
append
functions are simpler ways to build new lists.
list
takes any number of arguments, evaluates them, and builds a new list containing each value as an element. For example:
> (list ´1 ´2 ´3) (1 2 3) > (list ´a ´(b c) ´d) (a (b c) d) |
append
takes two arguments, which should both evaluate to lists, and creates a new list by concatenating the given lists. For example:
> (append ´(a b) ´(c d)) (a b c d) |
append
can also produce dotted pairs:
(append ´(a b) ´c) -> (a b . c) |
A predicate is a symbolic expression which evaluates to true (
t
) or false (
nil
), i.e. it is a test.
The following logical operators are defined:
not
,
and
,
or
.
atom
determines whether or not its argument is an atom, for example:
> (atom ´a) t > (atom ´(a b c)) nil |
listp
determines whether something is a list. For example:
> (listp ´a) nil > (listp ´(a b c)) t |
Predicates can be used to make choices, but to do this the equivalent of a conditional branch is needed. For this the
cond
(for conditional) function is provided.
cond
is similar to the "if; then; and else" statements.
A
cond
s-expression can have any number of arguments (clauses), which consist of a series of expressions. The first element of a
cond
clause is treated as a condition to be tested for; the rest consists of things to do should the condition prevail.
> (cond (predicate1 action1a action1b action1c ...) (predicate2 action2a action2b action2c ...) . . (t default_action1 default_action2 ...) ) |
if predicate1 is true, then evaluate action1a, action1b, etc. in sequence else if predicate2 is true, then evaluate action2a ... . . else evaluate default_action1, ... |
A
cond
clause is only fully evaluated providing that the first element of the clause evaluates to true
t
.
For example, if you want to be sure that something is a list before you take its
car
, do:
> (cond ((listp x) (car x))) |
This previous example is a
cond
of one clause, the s-expression
((listp x) (car x))
. Where the first element of the clause is
(listp x)
which is the condition of the clause. It is only when this evaluates to true that LISP evaluates the rest of the clause, the expression
(car x)
. For example:
> (setq x ´(a b c)) (a b c) > (cond ((listp x ) (car x))) a > (setq x ´y) y > (cond ((listp x) (car x))) nil |
Like other LISP functions,
cond
always returns a value. In the previous example, when the test in the
cond
clause evaluates to true, LISP evaluates the next expression, whilst returning the value of that expression as the value of
cond
. When the test failed, the
cond
returned
nil
.
A LISP user can create functions using the function
defun
(define function).
defun
takes as its arguments the name of the function to be defined, a list of formal parameters (literal atoms), and some bodies of code (s-expressions).
defun
does not evaluate any of these arguments, it associates, for future reference, the formal parameter list and bodies of code with the given function name.
For example, to create the simple function
addthree
:
> (defun addthree (x) (plus x 3)) addthree > (addthree 4) 7 |
More generally, the syntax of a call to
defun
would look like:
> (defun function_name (param1 param2 ... paramn) (... s-expression1 ...) (... s-expression2 ... (... s-expression2 ...) . . ) |
At times we want to repeat an operation an indefinite number of times, each time with different inputs. This can be achieved through the use of iteration or recursion.
By using recursion we can accomplish the equivalent of indefinite repetition; a function is said to be recursive if it refers to itself in its definition. It is necessary to make sure that the function checks first for a termination condition, to avoid an infinite loop. For example:
> (defun fac (n) (cond ((eq n 0) 1) (t (times n (fac (sub1 n)))) ) ) fac > (fac 4) 24 |
In iterative code, indefinite repetition is designated by explicit instructions to do something repeatedly. In LISP there are several functions that enable you to write an explicit loop
mapc
is a "LISP-like" way of doing an iteration, in which it takes two arguments; the first being a list and the second a function. For example:
> ( mapc list_name function_name ) |
mapc
maps each element of
list_name
by applying
function_name
(which must be a single argument) to it, for example:
> ( mapc ´( 2 5 7 ) add1 ) ( 3 6 8 ) |
Parasolid LISP provides a more extensive set of LISP facilities than those which are generally found in other LISP dialects, important points worth noting are listed below:
plus
,
difference
,
times
,
quotient
,
equal
,
greaterp
,
lessp
are overloaded
For example,
plus
works with character strings as well as reals.
--
, as beginning a commentFor a quick reference table summary of the functions available in PARASOLID LISP, see Appendix B, "Parasolid LISP Functions".
The error codes in PARASOLID LISP are given in Appendix D, "List of Parasolid LISP Functions".
Parasolid LISP has been extended by the inclusion of object-oriented functions and these are used extensively within KID.
Object-oriented expressions have the form:
(object function argument1 argument2 ...) |
The functions listed first are extensions to functional LISP rather than object oriented themselves:
undefine
- deletes an object (and any of its subclasses) whether or not it has been previously defined. The syntax is shown below; the argument can be one object or many, separated by spaces:Object oriented LISP provides many more useful functions to add to those described for standard LISP. Below is a summary of such functions which are properties of the universe class. Try using (universe help _<name_>) for more information.
detach
- detaches an object from its owning class or parent.
attach
- attaches an object to a new class.
is
- returns the owning class of an object. If an argument is given it must be the name of a class and the function returns the subclass of the one given which leads to the object in the class structure, e.g.
superclass
- returns the owning class of an object
subclass
- returns the objects owned by the given class
sibling
- returns all objects in the same owning class as the given object
supertree
- returns the direct ancestors of the object, e.g.
> ( left_leg supertree ) --> (left_leg limb fred man universe) |
subtree
- returns the descendants of a given object
-
removes a property from an object. If the property is not found, this message is not passed to the owning object.
> ( fred age 33 ) --> 33 |
The following object functions are used internally in KID and need not concern the user. They are reserved words and should not be overwritten.
PROPERTY FUNCTIONP INHERIT SYSTEM SUBJECT LAZY GUARD LISTENER UNGUARD OWN OWNER RESUME ABANDON |
<<< Kernel Interface Driver (KID) - an Overview | Chapters | Object-Oriented KID >>> |