Title: Lisp: An Introduction
1Lisp An Introduction
- Lisp List Processing language
- Developed by John McCarthy, MIT, in the late
1950s - the original idea was to implement a language
based on mathematical functions that McCarthy was
using to model problem solving - a grad student implemented the first interpreter,
and it was quickly adopted for AI programming - symbolic computing
- list processing
- recursion
- early Lisp was based almost entirely on recursive
and so - Lisp had no local variables nor iterative control
structures (loops) - and since Lisp was interpreted, there was no
compiler for early Lisp
2More on Lisp
- In part because this was early in the history of
high level languages, and in part because of the
desire to support AI - Lisp used dynamic scoping
- Lisp had no compiler
- Lisp had no local variables
- Lisp was slow
- But, because Lisp was interpreted
- It was easy to prototype systems and build larger
scale systems than a compiled language - Lisp had features not available in other early
languages - Recursion
- Linked lists
- Dynamic memory allocation
- Typeless variables
3Lisps, Lisps and More Lisps
- The original Lisp was both inefficient and
difficult to use - Other Lisp dialects were released by people
trying to further the language - they added a compiler, local variables, static
scoping, etc - while Scheme is the most recognized Lisp to come
out of these efforts, others include Qlisp,
Elisp, Interlisp, etc - Specialized hardware was manufactured to support
Lisp - hardware that had large heap memory spaces and
optimized routines for garbage collection - interestingly, the first GUI was implemented for
Lisp machines - however, many researchers were put off by the
high cost of these specialized machines, so there
was also a push for implementing many of the
necessary Lisp hardware mechanisms in
general-purpose computers leading to both
diversity (among compilers and dialects) and
innovation - but because of all the different Lisps, sharing
of ideas and code became awkward if not
prohibitive
4Toward a Standard Common Lisp
- DARPA sponsored an effort in the early 1980s to
develop a standard for Lisp, and so Common Lisp
(CL) was born - However, among the Lisp community, there were
hard-fought battles - West coast users often used Interlisp
- East coast users often used MACLisp
- European communities may have used yet other
Lisps - and Scheme was often taught in colleges
- CL would take the best attributes found in
MacLisp with some influence from Zetalisp, Scheme
and Interlisp - it would also provide a number of imperative
features (e.g., loops) - ANSI created a standard for CL around 1990
- the Common Lisp Object System (CLOS) was added to
CL making the language roughly equivalent in size
and capability to C - while not as recognized as C, Common Lisp is a
very useful and flexible language, we will study
it here
5Goals of CL
- Commonality a common dialect of Lisp for which
extensions (for given hardware) should be
unnecessary - Portability and Compatibility
- Consistency prior Lisps were internally
inconsistent so that a compiler or interpreter
might assign different semantics to a correct
program - Expressiveness and Power
- Efficiency for instance, optimizing compilers
and instructions that perform efficient
operations on lists - Stability changes in CL will be made only with
due deliberation
6Some CL Uses
- CL is a very successful language in that
- it is extremely powerful as an AI tool
- those that use it love it
- And yet CL is a very uncommon language because
- it is very complicated
- most software development is in C (with some in
Java, C, C, Python, Ada, etc) such that few
programmers would choose to use CL for
development - The most important software developed from CL
includes - Emacs
- G2 (a real-time expert system)
- AutoCAD
- Igor Engraver (musical notation editor and
publisher) - Yahoo Store
- Up until the early 1990s, most AI research was
still done in Lisp, most using Common Lisp - however, once C added objects, many researchers
switched to C because - their graduate students had more familiarity with
C - obtaining C compilers is often cheaper and
easier than CL compilers and environments - C programs will run faster than CL programs
(not always true, but generally true)
7Why Study CL?
- Learn about functional programming
- Learn how to utilize recursion better
- Learn about symbolic computing
- Gain experience in other languages and syntax
- Get a better idea of such concepts as scope,
binding, memory allocation, etc - Learn a little about AI problem solving
- But primarily learn a new language
- one that is very VERY different from what you
have already experienced - but also a very rich and complex language
- CL contains 978 defined symbols, macros and
functions not including anything available in
libraries or what you might define yourself
8Lisp Basics
- Everything in a Lisp program is either
- An atom
- a single literal value which could be
- a number
- a symbol like hello
- not to be confused with a string, a symbol cannot
be subdivided in any way - a character
- T or nil
- A list
- lists are denoted by ( )
- lists can contain nothing (empty list), one or
more atoms, sublists, or some combination of
these - In Lisp, all variables are pointers that point to
an atom or a list, but like Java, these pointers
do not need dereferencing (unlike C or C) - there are also sequences of different types
(vectors, arrays, strings, structures, objects),
these are neither lists nor atoms
9Variables, Lists, Pointers
- All variables are in fact pointers
- A variables pointer points to an item in heap
memory - like Java, there is no dereferencing (unlike C or
C) - unlike Java, all items are pointed to whether
they are objects, strings, arrays, numbers,
atoms, whatever - Lists are not only pointed to by the variable
that defines the list, but each list element
contains a pointer to the next item - The structure of a list item is actually two
pointers - the CAR is the pointer to the item
- the CDR is the pointer to the next list item
- this structure looks like this
- This structure is known as a cons cell
10List Structures
- A List then consists of a group of these cons
structures, each structure has - one pointer (the car) that points to the atom or
sublist - one pointer (the cdr) that either points to the
remainder of the list or is nil (like null in
Java or NULL in C)
( A ( D E F ) B C )
Cons cells are needed to make lists The cdr
should be a pointer to the next cons cell in the
list (or nil if this is the last cons cell, as
with the cells storing C and F) but in some
cases, you might put a datum in the cdr, this
would result in a dotted pair (a . b)
a b
11Pointers and Pointers
- Things can get confusing in Lisp because the
variables are pointers, and the things that they
point to may also be pointers - Look at the list example on the last slide, a
given cons cell has two pointers, one pointing to
the car and one to the cdr (which is usually
another cons cell or the value nil) - When you declare a variable, you are allocating
memory to store the pointer, not the datum - (let (a b) ) ? allocates space for a and b
- (setf a 5) ? allocates space for 5, adjusts a to
point at 5 - (setf b 6) ? allocates space for 6, adjusts b to
point at 6 - (setf b 5) ? will this allocate a new 5 or adjust
b to point at the already existing 5? This
differs depending on the implementation!
a
b
nil
nil
a
5
b
6
5
?
12Lisp Code vs. Data
- Lisp code is placed inside of lists
- All Lisp instructions are function calls
- All Lisp function calls are written in prefix
notation (name param1 param2 ) - All Lisp function calls return a value, which can
be used in another call - example (square ( 3 5)) ? ( 3 5) returns 8,
(square 8) returns 64 - Lisp data are most commonly placed in lists
- So in Lisp, data and code have the same look to
them, this makes it easy to write code that
manipulates code rather than data (code that can
generate code for instance) - - there are exceptions to this statement that
we will explore throughout this course
13Lisp Interpreter
- One of the more unique aspects of Lisp (as
opposed to say Java or C) is that it is an
interpreted language - You are given a command line where you can type
in commands where a command can be - a single executable statement (e.g., ( x y))
- a definition (defining a new function, defining a
variable, defining a class, instantiating an
object, etc) - you see the response immediately and you dont
have to have already compiled anything, this lets
you test out code while you write it - The Interpreter works on a REPL cycle
- Read read the latest item typed in at the
command line (ended by ltentergt) - Eval evaluate what was typed in (execute it)
- Print print the result to the screen for
immediate feedback - Loop do it all over again
- Note you can type definitions and instructions
in an editor and load it all at once or compile
it and load the compiled file
14Example Session with REPL
CL-USER 1 gt 'hello HELLO CL-USER 2 gt (print
'hello) HELLO HELLO CL-USER 3 gt (setf a
'hello) HELLO CL-USER 4 gt a HELLO CL-USER 5 gt
'a A
Type in a statement, it is evaluated hello
evaluates the symbol hello print is a function,
it prints the item and also returns the
item setf is a function that has a side effect
of adjusting a pointer and returns the value that
the pointer is pointing to evaluate a returns
what a points to notice here the quote mark says
do not evaluate, just return
15Example Session
CL-USER 40 gt (defun findmin (lis) (let ((temp
(car lis))) (dolist (a (cdr lis)) (if (lt a
temp) (setf temp a))) temp)) FINDMIN CL-USER
41 gt (defun sortlist (lis) (let (temp (size
(length lis)) (sortedlist nil)
(currentmin 1000)) (dotimes (i size) (setf
temp (findmin lis)) (if (lt temp currentmin)
(setf currentmin temp)) (setf lis (remove temp
lis)) (setf sortedlist (append sortedlist
(list temp)))) sortedlist)) SORTLIST CL-USER
42 gt (sortlist '(5 3 4 7 2 1 6)) (1 2 3 4 5 6 7)
16The Debugger
- Unfortunately, the run-time environment is where
most of your errors will be caught - Every time the interpreter does not understand
something, you are thrown into the debugger - We will explore the debugger in detail later in
the semester, here are a couple of comments - the debugger lets you inspect the run-time stack,
you can see the values of local variables and
parameters, and the order that functions were
called - you can correct some mistakes and then resume
execution without having to abort what you are
doing, fix errors and start over - For now though, we will simply abort out of the
debugger any time an error arises - This differs from implementation to
implementation, but in LispWorks, look at the
options and then type c where is the number
of the abort option (for instance, c 1)
17Example With Debugger
CL-USER 43 gt (sortlist b) Error The variable B
is unbound. 1 (continue) Try evaluating B
again. 2 Return the value of B instead. 3
Specify a value to use this time instead of
evaluating B. 4 Specify a value to set B to.
5 (abort) Return to level 0. 6 Return to top
loop level 0. Type b for backtrace, c ltoption
numbergt to proceed, or ? for other
options CL-USER 44 1 gt c 3 Enter a form to
be evaluated '(5 3 2 4 1) (1 2 3 4 5)
18Example Continued
CL-USER 62 1 gt (sortlist b) Error The
variable B is unbound. 1 (continue) Try
evaluating B again. 2 Return the value of B
instead. 3 Specify a value to use this time
instead of evaluating B. 4 Specify a value to
set B to. 5 (abort) Return to level 1. 6
Return to debug level 1. 7 Return a value to
use. 8 Supply a new first argument. 9
Return to level 0. 10 Return to top loop level
0. Type b for backtrace, c ltoption numbergt to
proceed, or ? for other options CL-USER 63 2
gt c 4 Enter a form to be evaluated '(5 3 4 6 2
1) (1 2 3 4 5 6)
19Example Continued
CL-USER 66 gt (sortlist) Error Call ((LAMBDA
(LIS) (DECLARE (SPECIALSOURCE )
(LAMBDA-NAME SORTLIST)) (BLOCK SORTLIST
(LET SORTEDLIST)))) has the wrong number
of arguments. 1 (abort) Return to level 0. 2
Return to top loop level 0. Type b for
backtrace, c ltoption numbergt to proceed, or ?
for other options CL-USER 67 1 gt
b Interpreted call to SORTLIST Call to
SPECIALEVAL-NOHOOK Call to IVPROCESS-TOP-LEVEL
Call to CAPICAPI-TOP-LEVEL-FUNCTION Call to
CAPIINTERACTIVE-PANE-TOP-LOOP Call to
(SUBFUNCTION MPPROCESS-SG-FUNCTION
MPINITIALIZE-PROCESS-STACK)
20Getting Output Dribble
- You can send output to a text file, which we will
cover later in the semester - for now, to output your entire session in the CL
environment, use dribble - form (dribble filename)
- from this point forward, everything that you type
in and everything that is returned (even debugger
messages) are sent to the textfile filename - to stop (or turn off) the dribble, type (dribble)
- if filename is just the name of a file, the
file is created and stored in the current
directory - you can specify a directory, but since \ is an
escape character (much like in Java), you have to
specify \\ - as in (dribble C\\csc375\\output1.txt)
- if you specify a filename that already exists,
anything that you dribble to the file is appended
(the file is not overwritten) - this allows you to join a previous session
21Defining Functions
- There are a number of built-in functions in CL
- We will explore them throughout the semester
- For now, we briefly consider how to define a
function - The basic form is
- (defun name (params) body)
- name is the name of your function to have, it can
be any legal CL name (and as long as the name is
not a pre-defined CL name but it can be a name
that you had previously used yourself) - params are the names of parameters being passed
into the function, they can be any legal name,
and there are no types associated with them in
the definition, the list can be empty as in ( ) - body is one or more CL function calls, after the
last one is called, whatever value that last
function call returns is returned by this function
22Examples
(defun square (x) ( x x)) (defun largest (x y)
(if (gt x y) x y)) if (x gt y) return x
else return y (defun printnumbers (x) (if
(gt x 0) (progn
(printnumbers (- x 1)) (print
x)))) recursive version, if (x gt 0)
recursively call the function with x-1, then
print x (defun printnumbers2 (x) (do
((i 1 ( i 1))) (( i ( x 1)))
(print i))) here, a do loop is used
to iterate from i1 to x, stopping once i x1
- Type in (square 5) and the function returns 25
- x is 5
- the function performs ( 5 5)
- progn is used to denote a block of function calls
where the result of the last function is returned
from the block
23Recursive vs Iterative
- Here we can clearly see the value of recursion
when implementing simple operations - the recursive version is short and matches our
mathematical notion of factorial - the iterative version requires a local variable
for the product
(defun factorial (x) (if (lt x 1) 1 ( x
(factorial (- x 1))))) if x lt 1 then return
1, otherwise return x factorial(x-1) (defun
factorial2 (x) (let ((y 1)) y is a
local var 1 (do ((i 1 ( i 1)))
loop on i (( i ( x 1)))
until i x1 (setf y
( y i))) y y i y)) return y
as the last action
24Lisp and Variables
- There are generally three kinds of variables
- Global variables defined in the run-time
environment (at the command line) or outside of a
function in a file - these can be accessed within a function, but only
having been declared as special - Local variables defined in a function using the
let statement, or within the scope of a specific
instruction, such as the loop counter(s) in a do
statement - these can only be accessed inside their scope, as
soon as their scope ends, you can no longer
access them - Parameters much like local variables, but they
are available throughout the entire function
25Variable Types
- As described earlier, variables are actually
pointers, but unlike C, the pointers are not
typed - This means that the value being pointed to by a
pointer can be of one type and later the pointer
can point to a value of another type - With a few exceptions, variables in CL are not
typed - notable exceptions to not typing variables are
objects and structures - However, all variables are type checked at
run-time to make sure that a value is being
treated correctly - So ( 5 a) will yield a run-time error rather
than an incorrect value
26Values
- There are generally 3 kinds of values
- Numbers
- pretty self explanatory although Lisp contains
several types of numbers - integer, float, fraction, complex (contains an
integer or float portion and an imaginary
portion) and more - you can combine types in an operation, Lisp will
convert the answer to whichever type is necessary - numbers are not restricted in size other than the
size of what your computers memory can store! - Atoms
- do not confuse these with strings, an atom is
atomic (you cant for instance do substring or
charAt type operations), the atom however can
represent any symbolic item (word or words) - Lists
- as we discussed earlier
- There are also characters, strings, arrays,
structures, objects and more that we will discuss
in detail later in the semester
27Numbers in More Detail
- Unless otherwise indicated, numbers are stored as
- Integers
- Fractions
- You can override the default by indicating
- Floating point (in either decimal or scientific
notation) - Hexadecimal (x precedes the value as in x54B3E)
- Octal (o as in o5102)
- Any base up to 36 of the form brvalue as in
6r3051 or 36rAZ9Q5 - base 36 includes all 10 digits and the 26 upper
case letters - Complex c(value1 value2) value1 value2i
(i is the square root of -1) - The result of any arithmetic operation will be in
the form of the widest value (e.g. int fraction
fraction) - Except for / which will place integer divisions
as a fraction
28Operations on Numbers
- Arithmetic functions
- , -, , /
- Note that / will always return quotient and
remainder, there is nothing equivalent to
integer division as we see in Java - But there are functions for truncate, round,
ceiling, floor and mod - (truncate 5 3) ? 1 (equivalent to 5 / 3)
- (mod 5 3) ? 2 (equivalent to 5 3)
- Common Lisp will always reduce fractions as much
as possible, so (/ 6 8) ? ¾ and (/ 6 2) ? 3 - Now consider ( c(3 5) c(2 1)) ? c(5 6)
- But ( c(3 2) c(3 -2)) ? 13, why?
- Comparison functions
- lt, gt, , /, lt, gt
- EQL is similar to but more restrictive, it says
numbers must be equal and be the same type - ( 5 5.0) is T, (EQL 5 5.0) is nil
29Preventing Evaluation
- One problem in Lisp is that everything entered at
the command line is evaluated - ( 3 5) ? evaluate , apply it to the evaluation
of 3 and 5 - ( a b) ? evaluate , apply it to the evaluation
of a and b - a and b are variables, so evaluating a returns
the value it points to (if a 5 and b 4, then
( a b) returns 9) - (length lis) returns the number of elements in
lis - What if lis is not a variable, but the list (a b
c)? - (length (a b c)) ? this does not return 3,
instead it will probably cause an error because
(a b c) is interpreted as call function a
passing parameters b and c
30Quote
- So, to avoid some thing being evaluated, use
(quote thing) as in (length (quote (a b c))) - we can use quote to generate a symbol as in
(quote hello) or a list as in (quote (a b c)) - We will often use (quote ) so Lisp gives us a
shortcut way of specifying it, - Sometimes it will be confusing as to when to
quote something and when not to - In general, if you have a list of data, you will
want to quote the list so that the first item is
not interpreted as a function call - If you are referencing a variable, do not quote
it, if you are referencing an atom, quote it - NOTE has an entirely different meaning so
make sure you use the forward quote or (quote ),
we will discuss the backward quote later in the
semester
31Special Forms
- I said earlier that all Lisp code is function
calls - There are some notable exceptions called special
forms - Here are some special forms
- defun used to define a new function as already
discussed - if and cond Lisp conditional statements
- quote return the item without evaluating it
- let bind variables to memory locations,
possibly initializing them - and, or and returns either T or nil, or returns
either nil or the first non-nil item - progn declare a block of functions to make up
the body of some function or instruction,
returning the value returned by the last function - all of these are actually macros, we will cover
how to define macros later in the semester, but
this gives you the flexibility of writing
operations that are different from functions
32Basic I/O
- CL has very powerful I/O facilities
- However, to get started, we will look at two
simple I/O operations - (read) get the next input from the keyboard and
return it - (setf x (read)) accepts one input and stores
it in x - (print x) sends x to the run-time window, but
only works on a single item - if you want to print multiple items, put them in
a list as in - (print (list x y z))
- Note this output will look like this (5 3 1)
rather than 5 3 1 - Later on, we will examine formatted input and
output, inputting from other sources, outputting
to different windows, and file I/O
33Basic Control Structures
- We will explore the control forms later, here are
four to get started - if and if-else
- (if (condition) then)
- (if (condition) then else)
- then what to return if the condition is true
- else what to return if the condition is false
- these will usually be function calls, but could
be values to return - (if (/ y 0) (/ x y) 0) divide x by y or return
0 if y 0 - iteration dotimes and dolist
- (dotimes (var num) )
- iterates from 0 to num-1, setting var to each
value one at a time - (dotimes (i n) (print ( i 1))) prints the
numbers 1 through n - (dolist (var lis) )
- iterates for each item in the list lis, with var
assigned to each - (dolist (a lis) (print a)) prints each item of
lis one at a time
34Putting It All Together
- We will rely heavily on the REPL interpreter
- Start up Common Lisp
- Type in a function, if it is interpreted
correctly, there will be no error - Now you can call your function to test it out
- If it isnt right, redefine it with another defun
statement - Evolve your code over time
- Alternatively, type your code into an editor
- This allows you to write your code as a series of
functions so that you can get a more global idea
of what you have to do, and save your code in a
text file - Copy and paste your code from the editor into the
interpreter - Or, save the file and load the entire file at one
time (usually you will only do this after you
know all of your individual functions work!)
35Apropos
- Cant find the right symbol (function) for some
particular application but you know its something
like foobar? Use Apropos - (apropos foobar)
- (apropos foobar packagename)
- apropos will look up all defined entities with
the string foobar in it and return the list - if you specify a package, it only lists those
items in the given package - most likely, you will want to limit your search
to the Common Lisp package, cl - (apropos reverse) returns somewhere around 46
entries whereas (apropos reverse cl) returns
just 2