[Integration Kit Contents] [Integration Kit What's New] [Integration Kit Function Index] [More Documentation] [PTC]

The ME Macro Programmer's Introduction to Lisp

Contents

Introduction

If you're already an experienced ME Macro programmer who understands the basics of:

... you won't have too much trouble learning to do the same things in Lisp. Lisp is a much bigger, richer language than the ME Macro language, but the basics are similar. This document will help you get started by describing a subset of Lisp commands that you can use when writing programs for Creo Elements/Direct Modeling. It is by no means a comprehensive description of Lisp. In fact, it deliberately neglects some details in order to keep things simple. The intention is to get you started writing Lisp programs without getting overwhelmed by the shear number of commands available.

Function Index Top of Page

Reference Books

You'll definitely want to get a Lisp reference book for more detailed information. Hopefully, you'll find that Lisp is pretty easy to learn if you start writing some simple programs using the basics described here, and then add new commands to your repetoire by referring to the books and trying more complex examples.

  1. "Introduction to Common Lisp"
    Taiichi Yuasa and Masami Hagiya
    Academic Press, Inc.
    ISBN 0-12-774860-1
    This book is short. It also uses the same version of Common Lisp (KCL) for its examples that is used by Creo Elements/Direct Modeling. Common Lisp is pretty standard no matter which version you are using, but there are some peculiarities in the way the KCL interpreter works that are shown in this book.
  2. "Lisp", 3rd Edition
    P.H. Winston and B.K. Horn
    Addison-Wesley Co.
    ISBN 0-201-08319-1
    This book is longer and has a more conversational style. The examples are more interesting than those in the Yuasa book.
  3. "Common LISP: the Language", 2nd Edition
    Guy L. Steele, Jr.
    Digital Press
    ISBN 1-55558-041-6
    This is the Lisp Bible. It's thick as a brick and not a good place to start. It's a valuable reference once you're comfortable with the language, though.

There are plenty of other books about Lisp available, as well as FAQ lists on the Internet. These will get you started, though.

Function Index Top of Page

Experimenting

Lisp is an interpreted language, just like the ME Macro Language. That means you can develop your programs interactively without having to compile them. It's possible to enter/load and run your programs directly from the Creo Elements/Direct Modeling command line. You can enter any Lisp command at the Creo Elements/Direct Modeling prompt "Enter Command".

On the Creo Elements/Direct Modeling command line, you can retrieve previous inputs and edit them. To do this, use:

To move the cursor for editing, use the left and right arrows keys.

You can also write your experimental code in a file and load it into Creo Elements/Direct Modeling:

    (load "/tmp/myfile.lsp")

By convention, Lisp source files have names ending in ".lsp".

Any output generated by your Lisp commands will be written to the terminal window from which you started Creo Elements/Direct Modeling.

Function Index Top of Page

Lisp Basics

Syntax

Let's take a look at a Lisp expression to get a feeling for the syntax:

    (+ 2 2)
    4

Notice three things:

All expressions in Lisp are surrounded by parentheses. You'll see lots of them. At first, you may go blind trying to match them up. Don't worry, text editors like vi and emacs have built-in functions for matching parentheses.

Also, all function calls, from the simplest mathematical operation, to the most complex function that you write yourself, are made with this prefix notation, where the function comes first followed by its parameters:

    (my-addition-function 2 2)
    5

Unlike the Macro language, Lisp functions always return a value. This is true of built-in functions as well as the functions that you write yourself.

It's also important to note that Lisp is not case-sensitive:

    (my-addition-function 2 2)
    5

    (My-Addition-Function 2 2)
    5

    (mY-aDDITION-fUNCTION 2 2)
    5

However, variables and function names (called symbols) are typically all lower-case. By convention, individual words in these names are separated by a dash - rather than an underscore _.

You'll want to include comments in your programs. You can do this two ways:

    ; This function doesn't seem to be working...

    #|
    (my-addition-function 2 3)
    (my-addition-function 6 2)
    (my-addition-function 3 7)
    |#

    ;; Use standard operator instead
    (+ 2 3)
    (+ 6 2)
    (+ 3 7)

Global Variables

In the Macro language, you could define a global variable outside one of your macros and give it a value using a LET statement. You can do the same in Lisp using defvar:

    LET My_global_variable "foo"

    (defvar *my-global-variable* "foo")

The initial value of the global variable *my-global-variable* can be set when it is defined as shown. Suppose you want to change its value later? In the Macro language, you would just use LET again. In Lisp, you need to use setf:

    LET My_global_variable "bar"

    (setf *my-global-variable* "bar")

Another call to defvar wouldn't work. Multiple calls to defvar with the same variable name and different values will only set the value the first time; subsequent calls will not change the value once it is already set.

Note: The assignment was done using setf. There are other ways to assign values in Lisp (like setq), but setf is the most general and will always work.

To define a constant whose value can not be changed after definition, use defconstant:

    (defconstant *my-global-constant* "always foo")

    (setf *my-global-constant* "bar")

    Error: Cannot assign to the constant *MY-GLOBAL-CONSTANT*.

By convention, the names of global variables and constants begin and end with an asterix *.

Just like in the Macro language, variables don't have a particular data type. The same variable can represent an integer, a real number, a string, or any other data type.

    (setf any-type (+ 2 2))
    (setf any-type 3.141592654)
    (setf any-type "a string")

You can determine the type of value that a variable is assigned using the type-of command:

    (setf any-type (+ 2 2))
    4

    (type-of any-type)
    FIXNUM

    (setf any-type 3.141592654)
    3.1415926540000001

    (type-of any-type)
    LONG-FLOAT

    (setf any-type "a string")
    "a string"

    (type-of any-type)
    STRING

Numbers and Calculations

Lisp offers the operators you'll need for making arithmetic calculations:

    (+ 1.5 2.3 3.45)
    7.25

    (- 4 2)
    2

    (* 2 3 7.4)
    44.400000000000006

    (/ 6 -2)
    -3

    (sqrt 4)
    2.0

Lisp includes boolean operators for comparing numbers as well. These boolean comparisons return either:

The numerical comparison functions are:

    (= 3 3)
    T

    (= 6 (* 3 2) (/ 12 2))
    T

    (= 3 4 5)
    NIL

    (/= 2 5 3 6)
    T

    (/= 2 5 2)
    NIL

    (< 2 3)
    T

    (< 2 4 1)
    NIL

    (< 2 2 3)
    NIL

    (<= 2 2 3 4)
    T

    (> 4 3 2)
    T

    (> 4 3)
    NIL

    (> 4 4 3)
    NIL

    (>= 4 4 3)
    T

    (numberp 2)
    T

    (numberp (+ 3.34 2.34))
    T

    (numberp "2")
    NIL

The book by Steele gives the definitive list of operators available in Lisp. The Yuasa book is also pretty complete on this topic.

There will also be special arithmetic functions defined for Creo Elements/Direct Modeling to operate on geometric elements like points and vectors. Those functions will be covered in Creo Elements/Direct Modeling documents.

Strings and String Manipulation

Lisp also has many functions for manipulating strings.

You can inquire properties of a single string:

    (stringp "string")
    T

    (stringp 5)
    NIL

    (length "sample string")
    13

    (subseq "some string" 0)
    "some string"

    (subseq "some string" 1)
    "ome string"

    (subseq "some string" 0 4)
    "some"

You can compare two strings:

    (string= "string1" "string1")
    T

    (string= "string1" "string2")
    NIL

    (string< "string1" "string2")
    6

    (string< "string2" "string1")
    NIL

    (string< "string1" "string1")
    NIL

You can build a string:

    (write-to-string 5)
    "5"

    (write-to-string (+ 2 2))
    "4"

    (format nil "~a and ~a is ~a" 2 2 (+ 2 2))
    "2 and 2 is 4"

    (format nil "~a~%and~%~a~%is~%~a" 2 2 (+ 2 2))
    "2
    and
    2
    is
    4"

Note: Creo Elements/Direct Modeling also provides some string handling functions to deal with localizable strings. Those functions will be covered in Creo Elements/Direct Modeling documents.

Lists and List Operations

Lists are the fundamental data type of Lisp. You can make lists out of all sorts of Lisp objects and assign them to variables:

    (setf integers-list (list 1 2 3 4))
    (1 2 3 4)

    (setf sums-list (list (+ 1 1) (+ 2 2) (+ 3 3)))
    (2 4 6)

    (setf strings-list (list "list" "of" "some" "strings"))
    ("list" "of" "some" "strings")

    (setf integers-and-strings-list (list 1 "two" 3 "four"))
    (1 "two" 3 "four")

The function list explicitly creates a list out of the values and/or the results of any expressions that you give to it. You can also create a list using a quote ':

    (setf sum-expressions-list '((+ 1 1) (+ 2 2) (+ 3 3)))
    ((+ 1 1) (+ 2 2) (+ 3 3))

The quote ' prevents any of the included expressions from being evaluated. If you want some of them to be evaluated, use a backquote ` and commas , in combination:

    (setf sums-and-expressions-list `((+ 1 1) ,(+ 2 2) (+ 3 3) ,(+ 4 4)))
    ((+ 1 1) 4 (+ 3 3) 8)

Using this technique, the expressions preceded by a comma are evaluated and the others are not. "So what?", you might well be thinking. Eventually, you may find powerful uses for this mixture of immediate and delayed evaluation. For now, it's just important to understand the meaning when you read example programs that use this technique.

You can find out how many items are in a list:

    (length '(1 2 3 4 5 6))
    6

    (length '())
    0

You can check a list to see if it includes a certain item:

    (member 1 '(1 2 3))
    (1 2 3)

    (member 2 '(1 2 3))
    (2 3)

    (member 4 '(1 2 3))
    NIL

You can extract values from lists:

    (setf example-list '("zero" 1 "two" 3 "four" 5))
    ("zero" 1 "two" 3 "four" 5)

    (first example-list)
    "zero"

    (rest example-list)
    (1 "two" 3 "four" 5)

    (last example-list)
    (5)

    (butlast example-list)
    ("zero" 1 "two" 3 "four")

    (nth 2 example-list)
    "two"

    (nth 10 example-list)
    NIL

    (first (rest example-list))
    1

    (rest (last example-list))
    NIL

You can also add items to lists:

    (setf original-list '(1 2 3 4))
    (1 2 3 4)

    (setf new-list (cons 0 original-list))
    (0 1 2 3 4)

    new-list
    (0 1 2 3 4)

    original-list
    (1 2 3 4)

    (push 0 original-list)
    (0 1 2 3 4)

    original-list
    (0 1 2 3 4)

    (setf newlist (append original-list '(6 7 8)))
    (0 1 2 3 4 6 7 8)

    newlist
    (0 1 2 3 4 6 7 8)

    original-list
    (0 1 2 3 4)

    (nconc original-list '(6 7 8)))
    (0 1 2 3 4 6 7 8)

    original-list
    (0 1 2 3 4 6 7 8)

There are functions to remove items from lists:

    original-list
    (0 1 2 3 4 6 7 8)

    (pop original-list)
    0

    original-list
    (1 2 3 4 6 7 8)

    (delete 4 original-list)
    (1 2 3 6 7 8)

    original-list
    (1 2 3 6 7 8)

    (remove 3 original-list)
    (1 2 6 7 8)

    original-list
    (1 2 3 6 7 8)

Structures and Arrays

Lisp includes other useful data types in addition to lists. One particularly useful type is the structure:

structures

Structures are defined using the defstruct command:

    (defstruct 3d_point (x 0)  (y 0)  (z 0))
    3D_POINT

    (setf origin (make-3d_point))
    #S(3D_POINT X 0 Y 0 Z 0)

    (setf some-3d-point (make-3d_point :x 1 :y 2 :z 3))
    #S(3D_POINT X 1 Y 2 Z 3)

    (3d_point-z some-3d-point)
    3

    (setf (3d_point-z some-3d-point) 4)
    4

    (3d_point-z some-3d-point)
    4

    (defstruct 2d_point x y)
    2D_POINT

    (setf some-2d-point (make-2d_point))
    #S(2D_POINT X NIL Y NIL)

    (2d_point-p some-2d-point)
    T

    (2d_point-p some-3d-point)
    NIL

Other data types are offered by Lisp and are described in the reference books. Of these, you may also find use for:

array

You can create an array using the make-array command:

Individual array cells are accessed using aref:

You can inquire the rank and dimensions of an array:

    (setf 1x10-array (make-array 10))
    #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)

    (setf 2x2-array (make-array '(2 2) :initial-element 0))
    #2A((0 0) (0 0))

    (setf (aref 2x2-array 0 1) "foo")
    "foo"

    (aref 2x2-array 0 1)
    "foo"

    (array-rank 1x10-array)
    1

    (array-dimensions 2x2-array)
    (2 2)

    (aref 2x2-array 2 2)

    Error: The first index, 2, to the array
       #2A((0 "foo") (0 0)) is too large.

Functions

In the ME Macro language, you write macros to perform specific tasks. Lisp allows you to define functions using defun instead.

    DEFINE Hello_world
      DISPLAY "Hello, world!"
    END_DEFINE


    (defun hello-world ()
      (format nil "Hello, world!"))

Note: Lisp also contains "macros". Lisp macros work like functions, but are evaluated differently than functions by the Lisp interpreter. Lisp macros can be very powerful - and a bit confusing. You don't need to use them at first. The reference books say more about them if you're interested. From now on, when the word "macro" is used in this document, it will refer to ME Macros, not Lisp macros.

Parameters

Lisp functions can take parameters, just like macros.

    DEFINE Print_message
      PARAMETER Message
      DISPLAY Message
    END_DEFINE


    (defun print-message (message)
      (print message))

Notice that you don't have to specify the data type of the parameter "message" in either language (in this case, "string"). In the macro, you just declare it using the keyword PARAMETER. In Lisp, you name the parameter inside a pair of parentheses immediately following the function name.

Unlike Macro parameters, however, you can also specify Lisp parameters to be:

You define these special behaviors for the parameters when declaring them.

Optional Parameters (&optional)

A parameter is optional if you declare it using &optional. The function show-parameters has one required parameter (p1) and two optional parameters (p2 and p3):

    (defun show-parameters (p1 &optional p2 p3)
      (format nil "p1 is: ~a~%p2 is : ~a~%p3 is : ~a" p1 p2 p3))

    (show-parameters 1)
    "p1 is: 1
    p2 is : NIL
    p3 is : NIL"

    (show-parameters 1 2)
    "p1 is: 1
    p2 is : 2
    p3 is : NIL"

    (show-parameters 1 2 3)
    "p1 is: 1
    p2 is : 2
    p3 is : 3"

The optional parameters can be assigned default values so that they don't come up NIL if no values are passed for them:

    (defun show-parameters (p1 &optional (p2 2) (p3 3))
      (format nil "p1 is: ~a~%p2 is : ~a~%p3 is : ~a" p1 p2 p3))
        
    (show-parameters 1)
    "p1 is: 1
    p2 is : 2
    p3 is : 3"

    (show-parameters 1 "two" "three")
    "p1 is: 1
    p2 is : two
    p3 is : three"

Arbitrary Parameters (&rest)

You can also pass an arbitrary number of parameters to a function. Any parameter following &rest will bundle all remaining values passed to a function into a single list.

    (defun show-parameters (p1 &rest other-parameters)
      (format nil "p1 is: ~a~%other-parameters are: ~a" p1 other-parameters))

    (show-parameters 1)
    "p1 is: 1
    other-parameters are: NIL"

    (show-parameters 1 2 3 4 5)
    "p1 is: 1
    other-parameters are: (2 3 4 5)"

Keyed Parameters (&key)

You can also identify parameters by keywords. Any parameter identified by a keyword is optional and can have a default value defined for it. The keywords are a form of documentation, allowing you to pass values to your function in any order in a readable way.

    (defun show-parameters (&key (p1 1) (p2 2) (p3 3))
      (format nil 
              "parameter1 is: ~a~%parameter2 is: ~a~%parameter3 is: ~a"
              p1 p2 p3))

    (show-parameters )
    "parameter1 is: 1
    parameter2 is: 2
    parameter3 is: 3"

    (show-parameters :p1 "one" :p2 "two" :p3 "three")
    "parameter1 is: one
    parameter2 is: two
    parameter3 is: three"

    (show-parameters :p1 "one" :p3 "three")
    "parameter1 is: one
    parameter2 is: 2
    parameter3 is: three"

    (show-parameters :p3 "three" :p1 "one")
    "parameter1 is: one
    parameter2 is: 2
    parameter3 is: three"

By default, a keyword is the variable name prefixed with a colon. A keyword must start with a colon, but it can have a value other than the variable name.

You can combine all of these parameter types in many ways. The reference books define the possibilities more completely and give further examples.

Local Variables

Lisp functions can have local variables, just like Macros.

    DEFINE Circle_area
      PARAMETER Diameter
      LOCAL Radius
      LET Radius (Diameter / 2.0)
      DISPLAY (3.141592654 * Radius * Radius)
    END_DEFINE

    (defun circle-area (diameter)
      (let (radius)
        (setf radius (/ diameter 2.0))
        (* pi radius radius)))

In the Lisp function circle-area, the local variable radius is defined using the let statement. It is later assigned a value using setf. Notice that the other commands in the function are defined within the parentheses of the let statement. The parentheses define the scope of the variable. Outside these parentheses, the variable named radius is unknown.

You can define more than one variable using a let statement. You can also assign them initial values.

    (defun annular-area (outer-diameter inner-diameter)
      (let ((outer-radius (/ outer-diameter 2.0))
            (inner-radius (/ inner-diameter 2.0)))
        (* pi (- (expt outer-radius 2) (expt inner-radius 2)))))

One nice thing about the fact that Lisp functions always return values is that you often don't need so many local variables.

    (defun annular-area (outer-diameter inner-diameter)
      (* pi (- (expt (/ outer-diameter 2.0) 2)
               (expt (/ inner-diameter 2.0) 2))))

Conditions

In your Macro programs, you took different actions for different conditions using the IF command with some kind of test expression:

    IF (((ABS Integer) MOD 2) = 0)
      DISPLAY ((STR Integer) + " is even")
    ELSE
      DISPLAY ((STR Integer) + " is odd")
    END_IF

Lisp has several conditional statements for this purpose as well:

Remember that in Lisp, an expression is TRUE if its return value is anything other than NIL. A return value of NIL makes an expression FALSE.

    (when (= 3 3) (format nil "three + three = ~a" (+ 3 3)))
    "three + three = 6"

    (when (= 3 4) (format nil "three never equals four"))
    NIL

    (unless (= 3 4) (format nil "three never equals four"))
    "three never equals four"

    (unless (= 3 3) (format nil "three equals three, of course!"))
    NIL

    (if (= 3 3) 
         (format nil "three equals three")
         (format nil "three really DOES equal three"))
    "three equals three"

    (if (/= 3 3) 
         (format nil "three equals three")
         (format nil "three really DOES equal three"))
    "three really DOES equal three"

    (defun cond-example (number1 number2)
      (cond ((< number1 number2) (format nil "~a < ~a" number1 number2))
            ((= number1 number2) (format nil "~a = ~a" number1 number2))
            ((> number1 number2) (format nil "~a > ~a" number1 number2))))

    (cond-example 1 2)
    "1 < 2"

    (cond-example 2 2)
    "2 = 2"

    (cond-example 2 1)
    "2 > 1"

    (defun case-example (number)
      (case number
            ((1 2 3) "one-two-three")
            ((4 5 6) "four-five-six")
            (7 "lucky seven!")
            (otherwise "no luck..."))) ;; otherwise matches anything

    (case-example 2)
    "one-two-three"

    (case-example 4)
    "four-five-six"

    (case-example 7)
    "lucky seven!"

    (case-example 10)
    "no luck..."

Test expressions usually involve a comparison. You've already seen functions that compare numbers and strings. Here are three more general comparison functions:

   (defun equality-test (object1 object2)
     (cond ((eq object1 object2) (format nil "~a eq ~a" object1 object2))
           ((eql object1 object2) (format nil "~a eql ~a" object1 object2))
           ((equal object1 object2) (format nil "~a equal ~a" object1 object2))
           (t (format nil "~a does not eq, eql, or equal ~a" object1 object2)))) 
           ;; t is always true

    (setf x '(1 2 3))
    (1 2 3)

    (setf y x)
    (1 2 3)

    (equality-test x y)
    "(1 2 3) eq (1 2 3)"

    (equality-test 2.5 (/ 5.0 2.0))
    "2.5 eql 2.5"

    (equality-test "foo" "foo")
    "foo equal foo"

    (equality-test '(1 2 3) '(1 2 3))
    "(1 2 3) equal (1 2 3)"

    (equality-test '(1 2 3) '(1 2 3 4))
    "(1 2 3) does not eq or equal (1 2 3 4)"

Lisp offers so many comparison operators to let you use the most computationally efficient one for the task. All of this freedom of choice can be confusing. Practically speaking, you can:

You can construct logical combinations of expressions using the following boolean operators:

    (and (< 1 2) (= 2 2) (string= "foo" "foo"))
    T

    (and (< 1 2) (/= 2 2) (string= "foo" "foo"))
    NIL

    (or (< 1 2) (= 2 2) (string= "foo" "foo"))
    T

    (or (< 1 2) (/= 2 2) (string= "foo" "foo"))
    T

    (or (> 1 2) (/= 2 2) (string= "foo" "bar"))
    NIL

    (not (or (> 1 2) (/= 2 2) (string= "foo" "bar")))
    T

You can group multiple Lisp expressions together into a single expression having one return value using:

    (progn (+ 1 1) (* 2 2) (- 3 2))
    1

    (prog1 (+ 1 1) (* 2 2) (- 3 2))
    2

    (if (= x y)
        (incf identical-count 1) ;; increment count by one
        (progn
          (incf different-count 1)
          (setf sums (+ (+ x y) sums))))

You will use progn frequently in Creo Elements/Direct Modeling for tasks like executing several functions when a single button is pushed.

Iteration

Your Macros probably included some iteration commands like LOOP, WHILE, and REPEAT. Lisp offers several iteration commands as well:

    (defun loop-example (number)
      (let ((counter 0))
        (loop 
          (print counter)
          (incf counter)
          (when (= counter number) (return "done")))))

    (loop-example 3)

    0
    1
    2
    "done"

    (dotimes (counter 3 "done") (print counter))

    0
    1
    2
    "done"

    (dolist (counter '(0 1 2) "done") (print counter))

    0
    1
    2
    "done"

    (mapcar #'+ '(1 2 3) '(4 5 6))
    (5 7 9)

One of the most natural methods of iteration in Lisp is recursion, where a function calls itself repeatedly until some end condition is satisfied.

    (defun recursive-example (number)
      (if (< number 0)
          (values "done") ; returns the value "done"
          (progn
            (print number)
            (recursive-example (- number 1))))) 

    (recursive-example 2)

    2
    1
    0
    "done"

Input and Output

Similar to the Macro language, Lisp allows you to write to or read from a file by first opening a stream associated with the file. You can open a stream using the open command. When you are finished with a stream, you must close it.

    OPEN_OUTFILE 9
    (setf file-stream (open "/tmp/output_file" :direction :output))

      -- OR --

    OPEN_INFILE 9 "/tmp/input_file"
    (setf file-stream (open "/tmp/input_file" :direction :input))

      -- AND --
  
    CLOSE_FILE 9
    (close file-stream)

You can write to an open file using the format command shown earlier:

    (setf file-stream (open "/tmp/outfile" :direction :output))
    (format file-stream "Output value to file: ~a~%" my-value)
    (close file-stream)

You can read a line from an open file using:

You could use these commands in combination to copy one file to another:

    (defun copy-file (input-file output-file)
      (let (input-line
           (input-stream (open input-file :direction :input))
           (output-stream (open output-file :direction :output)))
        (loop
           (setf input-line (read-line input-stream nil 'eof))
           (if (eq input-line 'eof)
               (progn (close output-stream)
                      (close input-stream)
                      (return))
               (format output-stream "~a~%" input-line)))))

Lisp provides the convenient function with-open-file to take care of opening and closing the files for you.

    (defun copy-file (input-file output-file)
      (let (input-line input-stream output-stream)
        (with-open-file (input-stream input-file :direction :input)
          (with-open-file (output-stream output-file :direction :output)
            (loop
              (setf input-line (read-line input-stream nil 'eof))
              (if (eq input-line 'eof)
                  (return)
                  (format output-stream "~a~%" input-line)))))))
       

Function Index Top of Page

Best Practices

In Lisp, you can create a lot of "garbage" when performing list operations. Garbage is memory consumed by data that you don't need. Lisp is very conscientious about collecting the garbage on a regular basis and freeing the memory, but this tedious task slows down your program. You can avoid creating garbage by choosing the right list operation for the task.

Function Index Top of Page

Debugging

Lisp offers several debugging commands:

    (apropos "foo")
    FOO2  has value: "foo2"
    FOO-FUNC  Function
    FOO  has value: "foo"

    (trace copy-file)
    (COPY-FILE)

    (copy-file "/tmp/old-file" "/tmp/new-file")
      1> (COPY-FILE "/tmp/old-file" "/tmp/new-file")
      <1 (COPY-FILE NIL)
    NIL

    (untrace copy-file)
    (COPY-FILE)

    (copy-file "/tmp/old-file" "/tmp/new-file")
    NIL

    (copy-file "/tmp/no-file" "/tmp/new-file")

    Error: Cannot open the file /tmp/no-file.
    Fast links are on: do (use-fast-links nil) for debugging
    Error signalled by OPEN.
    Broken at OPEN.  Type :H for Help.
    >>:b
    Backtrace: system:top-level > eval > copy-file > let > OPEN
    NIL

The Yuasa book describes these comands in more detail and shows more examples.

Function Index Top of Page

Editors

The common text editors vi and emacs provide some support for writing Lisp programs.

vi

You can start vi in -l mode or use :se lisp to edit Lisp files. It will then offer you the following commands:

%
Move backwards to the parenthesis ( opening the current Lisp expression.
[[
Back up to the last opening parenthesis ( at the beginning of a line (no whitespace in left margin).
=
Reindent a specified number of lines to follow standard Lisp format.
Precede with a number or move command to specify the lines.

Use :set sm to set the showmatch option on. This will cause vi to briefly jump backward to the matching opening parenthesis ( when a closing parenthesis ) is typed.

Please refer to the UNIX man pages on vi or your favorite vi reference book for more information.

emacs

Emacs is written in Lisp, so it offers lots of support for Lisp programming if you really get into it. Some basic commands, equivalent to what vi offers, are:

Esc Ctrl-f
Move forward to the parenthesis ) closing the current Lisp expression.
Esc Ctrl-b
Move backward to the parenthesis ( opening the current Lisp expression.

Emacs automatically goes into lisp mode when you edit files ending in ".lsp". It will then indent using a standard Lisp format.

Function Index Top of Page

Sample Program

Shown below is a sample Lisp program that will plot some 2D points on a grid using definable ASCII characters. If you'ld like to try running the program:

Read through the program to see how it works. Then you can modify it, or write your own program to get some practice writing Lisp code.

; -*-Lisp-*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Description:  Example Lisp program that plots some characters in 2D space
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Constants and globals defining plotting grid characteristics

(defconstant *axis-char* "+")
(defconstant *no-value-char* ".")
(defconstant *default-plot-char* "*")
(defvar *h-max* 40)
(defvar *v-max* 20)
(defvar *rows* (+ *v-max* 1))
(defvar *columns* (+ *h-max* 1))


;; Define structures

(defstruct plot_point 
  v
  h
  (char *default-plot-char*))


;; Plotting functions

(defun make-grid ()
  (initialize-grid (make-array (list *rows* *columns*))))

(defun initialize-grid (grid)
  (let (row column)
    (dotimes (row *rows* grid)
      (dotimes (column *columns*)
        (if (or (= row 0) (= column 0)) ; this is an axis point
          (setf (aref grid row column) *axis-char*)
          (setf (aref grid row column) *no-value-char*))))))

(defun set-grid-points (point-list grid)
  (dolist (point point-list grid) (set-grid-point point grid)))

(defun set-grid-point (point grid)
  (let ((h (plot_point-h point))
        (v (plot_point-v point)))
    (cond ((not (eq (type-of h) 'fixnum)) nil) ; h not an integer, ignore point
          ((not (eq (type-of v) 'fixnum)) nil) ; v not an integer, ignore point
          ((or (> h *h-max*) (< h 0)) nil) ; h out of range, ignore point
          ((or (> v *v-max*) (< v 0)) nil) ; v out of range, ignore point
          (t (setf (aref grid v h) (plot_point-char point))))))
           
(defun plot-grid (grid)
  (let (row column)
    (dotimes (row *rows* 'done)
      (format t "~%")
      (dotimes (column *columns*)
        (princ (aref grid row column)))))) ; print value w/o line feed


;; Demonstration functions

(defun show-example ()
  (plot-grid (set-grid-points (make-example-points) (make-grid))))

(defun make-example-points ()
  (list (make-plot_point :v 4 :h 22 :char "_")
        (make-plot_point :v 4 :h 23 :char "_")
        (make-plot_point :v 5 :h 21 :char "/")
        (make-plot_point :v 5 :h 23 :char "/")
        (make-plot_point :v 5 :h 24 :char "\\")
        (make-plot_point :v 6 :h 20 :char "/")
        (make-plot_point :v 6 :h 22 :char "/")
        (make-plot_point :v 6 :h 25 :char "\\")
        (make-plot_point :v 7 :h 12 :char "_")
        (make-plot_point :v 7 :h 13 :char "_")
        (make-plot_point :v 7 :h 19 :char "/")
        (make-plot_point :v 7 :h 20 :char "_")
        (make-plot_point :v 7 :h 21 :char "/")
        (make-plot_point :v 7 :h 23 :char "/")
        (make-plot_point :v 7 :h 24 :char "\\")
        (make-plot_point :v 7 :h 26 :char "\\")
        (make-plot_point :v 8 :h 11 :char "/")
        (make-plot_point :v 8 :h 12 :char "_")
        (make-plot_point :v 8 :h 13 :char "/")
        (make-plot_point :v 8 :h 14 :char "\\")
        (make-plot_point :v 8 :h 17 :char "_")
        (make-plot_point :v 8 :h 18 :char "_")
        (make-plot_point :v 8 :h 19 :char "\\")
        (make-plot_point :v 8 :h 21 :char "\\")
        (make-plot_point :v 8 :h 23 :char "\\")
        (make-plot_point :v 8 :h 24 :char "_")
        (make-plot_point :v 8 :h 25 :char "\\")
        (make-plot_point :v 8 :h 27 :char "\\")
        (make-plot_point :v 9 :h 11 :char "\\")
        (make-plot_point :v 9 :h 13 :char "\\")
        (make-plot_point :v 9 :h 15 :char "\\")
        (make-plot_point :v 9 :h 16 :char "/")
        (make-plot_point :v 9 :h 18 :char "/")
        (make-plot_point :v 9 :h 19 :char "\\")
        (make-plot_point :v 9 :h 20 :char "\\")
        (make-plot_point :v 9 :h 22 :char "\\")
        (make-plot_point :v 9 :h 24 :char "\\")
        (make-plot_point :v 9 :h 25 :char "/")
        (make-plot_point :v 9 :h 27 :char "/")
        (make-plot_point :v 10 :h 12 :char "\\")
        (make-plot_point :v 10 :h 14 :char "\\")
        (make-plot_point :v 10 :h 16 :char "\\")
        (make-plot_point :v 10 :h 17 :char "/")
        (make-plot_point :v 10 :h 20 :char "\\")
        (make-plot_point :v 10 :h 21 :char "\\")
        (make-plot_point :v 10 :h 23 :char "\\")
        (make-plot_point :v 10 :h 26 :char "/")
        (make-plot_point :v 11 :h 13 :char "\\")
        (make-plot_point :v 11 :h 15 :char "\\")
        (make-plot_point :v 11 :h 18 :char "/")
        (make-plot_point :v 11 :h 19 :char "\\")
        (make-plot_point :v 11 :h 21 :char "\\")
        (make-plot_point :v 11 :h 22 :char "\\")
        (make-plot_point :v 11 :h 24 :char "\\")
        (make-plot_point :v 11 :h 26 :char "\\")
        (make-plot_point :v 12 :h 14 :char "\\")
        (make-plot_point :v 12 :h 16 :char "\\")
        (make-plot_point :v 12 :h 18 :char "\\")
        (make-plot_point :v 12 :h 20 :char "\\")
        (make-plot_point :v 12 :h 22 :char "\\")
        (make-plot_point :v 12 :h 23 :char "\\")
        (make-plot_point :v 12 :h 25 :char "\\")
        (make-plot_point :v 12 :h 27 :char "\\")
        (make-plot_point :v 13 :h 15 :char "\\")
        (make-plot_point :v 13 :h 17 :char "\\")
        (make-plot_point :v 13 :h 19 :char "\\")
        (make-plot_point :v 13 :h 20 :char "_")
        (make-plot_point :v 13 :h 21 :char "\\")
        (make-plot_point :v 13 :h 22 :char "/")
        (make-plot_point :v 13 :h 24 :char "\\")
        (make-plot_point :v 13 :h 26 :char "\\")
        (make-plot_point :v 13 :h 28 :char "\\")
        (make-plot_point :v 14 :h 16 :char "\\")
        (make-plot_point :v 14 :h 18 :char "\\")
        (make-plot_point :v 14 :h 20 :char "\\")
        (make-plot_point :v 14 :h 25 :char "\\")
        (make-plot_point :v 14 :h 26 :char "_")
        (make-plot_point :v 14 :h 27 :char "\\")
        (make-plot_point :v 14 :h 28 :char "/")
        (make-plot_point :v 15 :h 17 :char "\\")
        (make-plot_point :v 15 :h 18 :char "_")
        (make-plot_point :v 15 :h 19 :char "\\")
        (make-plot_point :v 15 :h 20 :char "/")))


#| Some ideas for improvement

   - Vary the grid size.

   - Define a file format for storing the plot points. Then write a function
   to recreate a stored plot from a file.

   - Change the program so that it doesn't display the grid, or only displays
   it if an option is set.

   - Write functions to transform the points horizontally, vertically, or
   by rotation.

   - Add a scaling routine to handle an infinite range of points.

|#

Function Index Top of Page

Symbol Index


[Integration Kit Contents] [Integration Kit What's New] [Integration Kit Function Index] [More Documentation] [PTC]
© 2023 Parametric Technology GmbH
(a subsidiary of PTC Inc.), All Rights Reserved