LISP - Franklin University Computer Science Program

COMP 205 – Week 12
Dr. Chunbo Chu
Last week
 Install Lisp in a Box
 loop
 read in an expression from the console;
 evaluate the expression;
 print the result of evaluation to the console;
end loop.
 REPL
Valid objects (S-expressions)
•
Atoms:
numbers: (real 1.0, integer 1)
symbols: a consecutive sequence of characters (no space)
e.g., a, x, price-of-beef.
two special symbols: T and NIL for logical true and
false.
strings: a sequence of characters bounded by double quotes
e.g., "this is red".
(Note: LISP is case insensitive)
•
Lists: a list of
atoms and/or lists, bounded by "(" and ")“,
e.g., (a b c), (a (b c))
top elements of a list
example: top elements of list (a b c) are a, b, and c
top elements of list (a (b c)) are a and (b c)
nil: empty list, same as ().
2. Function calls
•
•
•
•
also a list
use prefix notation: (function-name arg1 ... argn)
returns function value for the given list of arguments
functions are either provided by Lisp function library or
defined by the user.
• Examples:
>(+ 1 3 5)
9
>(/ 3 5)
3/5
>(/ 3.0 5)
0.59999999999999998
>(sqrt 4)
2
First Lisp program
( defun hello ()
(format t “Hello World!”))
 Use Emacs to create a new file (C-x C-f)containing the
above code.
 Save the file (C-x C-s) under name hello.lisp
 Switch back the SLIME buffer and load the program
by:
(load “hello.lisp)
 Run your program: (hello)
 Type q to leave the debugger if you have any error.
 first
 rest
 function
nesting
 car
 cdr
 cadr
 caddr
 nthcdr
 butlast
 cons
 append
Compositions of car and cdr
This place ...
Is equivalent to this place ...
(caar x)
(car (car x))
(cadr x)
(car (cdr x))
(cdar x)
(cdr (car x))
(cddr x)
(cdr (cdr x))
 Basic
expression
evaluation
2) Predicates (a special function which returns NIL if the
predicate is false, T or anything other than NIL, otherwise)
=, >, <, >=, <= for numerical values;
predicates
equal, eq, for others (symbols, lists, etc.)
>(< x y)
NIL
>(= x y)
T
tests if x is a atom
tests if x is a list
>(equal ‘x ‘y)
NIL
>(equal ‘a (car L))
T
>(atom x)
T
>(atom L)
NIL
>(listp x)
NIL
>(listp L)
T
>(atom (car L))
T
also numberp, symbolp, null
>(numberp ‘x)
NIL
>(numberp x)
T
>(symbolp ‘x)
T
>(symbolp x)
NIL
Basic
storage
handling
>(null L)
NIL
>(null NIL)
T
>(null x)
NIL
3) Set operations ( a list can be viewed as a set whose members
are the top elements of the list)
>(member 'b L) ; test if symbol b is a member (a top element) of L
(B C)
; if yes, returns the sublist of L starting at the
; first occurrence of symbol b
>(member ‘b (cons 'b L))
(B A B C)
>(member x L)
NIL
; if no, returns NIL
Set operations
>(union L1 L2)
; returns the union of the two lists
>(intersection L1 L2) ; returns the intersection of the two lists
>(set-difference L1 L2) ; returns the difference of the two lists
Defining New Functions
(defun name (parameter*)
"Optional documentation string."
body)
 Convention: you construct compound names with
hyphens rather than underscores or inner caps.
 Thus, frob-widget is better Lisp style than either
frob_widget or frobWidget.
 When a parameter list is a simple list of variable
names, the parameters are called required parameters
Optional Parameters
 Place the symbol &optional followed by the names of
the optional parameters.
(defun foo (a b &optional c d) (list a b c d))
 When the function is called, arguments are first bound
to the required parameters.
(foo 1 2) (1 2 NIL NIL)
(foo 1 2 3)  (1 2 3 NIL)
(foo 1 2 3 4)  (1 2 3 4)
Non-NIL default value
 Replace the parameter name with a list containing a
name and an expression.
 The expression will be evaluated only if the caller
doesn't pass enough arguments to provide a value for
the optional parameter.
(defun foo (a &optional (b 10)) (list a b))
(foo 1 2)  (1 2)
(foo 1)  (1 10)
 More flexibility:
 (defun make-rectangle (width &optional (height width))
...)
Rest Parameters
 Functions need to take a variable number of
arguments.
 E.g. (+), (+ 1), (+ 1 2), (+ 1 2 3), …
 A catchall parameter after the symbol &rest.
 Any arguments remaining after values have been doled
out to all the required and optional parameters are
gathered up into a list that becomes the value of the
&rest parameter.
(defun format (stream string &rest values) ...)
(defun + (&rest numbers) ...)
Keyword Parameters
 Suppose you have a function that takes four optional
parameters.
 Now suppose that most of the places the function is
called, the caller wants to provide a value for only one
of the four parameters.
 After any required, &optional, and &rest parameters
you include the symbol &key and then any number of
keyword parameter specifiers.
 (defun foo (&key a b c) (list a b c))
 (foo) (NIL NIL NIL)
 (foo :a 1)  (1 NIL NIL)
 (foo :b 1)  (NIL 1 NIL)
 (foo :c 1)  (NIL NIL 1)
 (foo :a 1 :c 3) (1 NIL 3)
 (foo :a 1 :b 2 :c 3)  (1 2 3)
 (foo :a 1 :c 3 :b 2)  (1 2 3)
 (defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
 Mixing Different Parameter Types
 Whenever more than one flavor of parameter is used,
they must be declared in order : first the names of the
required parameters, then the optional parameters,
then the rest parameter, and finally the keyword
parameters.
Function Return Values
 The default behavior: return the value of the last expression
evaluated as the function’s own return value.
 The RETURN-FROM special operator to immediately
return any value from the function
 The first "argument" is the name of the block from which
to return.
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
 Data
structure
s
 assoc
 make-array
 aref
 defstruct
Dotted
pairs
Dotted pairs
4) Conditional
>(cond (<test-1> <action-1>)
.
.
.
(<test-k> <action-k>))
conditional
• each (<test-i> <action-i>) is called a clause;
• if test-i (start with i=1) returns T (or anything other than
NIL),
this function returns the value of action-i;
else, go to the next clause;
• usually, the last test is T, which always holds, meaning
otherwise.
• cond can be nested (action-i may contain (cond ...))
Now, having basic functions, defun and cond we can define any
Lisp function. Examples.
(heavy use of recursive definitions)
(defun func-name (arg-1 ... Arg-n) func-body)
5. Define functions
examples:
member
(defun member (x L)
(cond ((null L) nil)
; base case 1: L is empty
((equal x (car L)) L)
; base case 2: x=first(L)
(t (member x (cdr L))) ; recursion: test if x is in rest(L)
))
(defun intersection (L1 L2)
(cond ((null L1) nil)
((null L2) nil)
((member (car L1) L2)
(cons (car L1) (intersection (cdr L1) L2)))
(t (intersection (cdr L1) L2))
))
intersection
Example: (intersection '(a b c) '(b a b c)) returns (a b c)
(intersection '(b a b c) '(a b c)) returns (b a b c)
(defun set-difference (L1 L2)
(cond ((null L1) nil)
((null L2) L1)
((not (member (car L1) L2))
(cons (car L1) (set-difference (cdr L1) L2)))
(t (set-difference (cdr L1) L2))
))
Define functions iteratively.
dolist
(dolist (x L result) body)
• for each top level element x in L, do body(x);
• x is not equal to an element of L in each iteration, but rather x
takes an element of L as its value;
dotimes
(dotimes (count n result) body)
; do body n times. count starts with 0, ends with n-1
Note: result is optional, to be used to hold the computing result.
If result is given, the function will return the value of result,
returns NIL, otherwise. (may change global variables as side
effects.)
Activity
 Write a function sum to calculate the sum of all
numbers in a list
Various definitions of SUM
(defun sum1 (L)
(setq y 0)
(dolist (x L y)
(setq y (+ y x))))
(defun sum2 (L)
(setq y 0)
(dolist (x L y)
(setq y (+ y (eval x)))))
(defun sum3 (L)
(setq y 0)
(dotimes
(count (length L) y)
(setq y (+ y (nth count
L)))
))
>(setq L1 '(1 2 3))
(1 2 3)
dolist
>(sum1 L1)
6
defun sum4 (L)
(setq y 0)
(dotimes
(count (length L) y)
(setq y
(+ y (eval (nth count
L))))
))
dotimes
>(setq L1 '(1 2 3))
(1 2 3)
>(setq L2 '(a b c))
(A B C)
>(dotimes (count 3)
(set (nth count L2)
(nth count L1)))
NIL
>a
1
>(sum1 L1)
6
>(sum1 L2)
Error: …
>(sum2 L2)
6
>(sum3 L1)
6
>(sum3 L2)
Error: …
>(sum4 L2)
6
if statement
 (if <test> <then> <else>)
> (defun abs (a)
(if (> a 0) a (- a)))
ABS
> (abs 2)
2
> (abs -3)
3
Logical operators
 and, or
> (and NIL T)
NIL
> (and T 2 3)
3
> (or nil (= 5 4))
NIL
> (or nil 5)
5
Recursion
 Recursive function definitions are very common in
LISP
> (defun factorial (num)
(cond ((<= num 0) 1)
(t (* (factorial (- num 1)) num))))
FACTORIAL
> (factorial 4)
24
Variable
 Common Lisp is dynamically typed—type errors are
detected dynamically.
 A variable can hold values of any type and the values
carry type information that can be used to check types
at runtime.
 Common Lisp is a strongly typed language in the
sense that all type errors will be detected.
(+ “hello” 1)
Introducing variables
 (defun foo (x y z) (+ x y z))
 LET special operator
(let (variable*)
body forms)
 Example: (let ((x 10) (y 20) z) ...)
 Within the body of the LET, the variable names refer
to the newly created bindings.
 After the LET, the names refer to whatever, if
anything, they referred to before the LET.
Nested bindings
(defun foo (x)
(format t "Parameter: ~a~%" x)
(let ((x 2))
(format t "Outer LET: ~a~%" x)
(let ((x 3))
(format t "Inner LET: ~a~%" x))
(format t "Outer LET: ~a~%" x))
(format t "Parameter: ~a~%" x))
;
;
;
;
;
;
;
|<------ x is argument
|
| |<---- x is 2
| |
| | |<-- x is 3
| |
|
 The value of the last expression in the body is returned
as the value of the LET expression.
Binding forms
 Any construct that introduces a new variable name
that's usable only within the construct is a binding
form.
 More example?
 (dotimes (x 10) (format t "~d " x))
LET*
(let* ((x 10)
(y (+ x 10)) )
(list x y))
 But not this
(let ((x 10)
(y (+ x 10)))
(list x y))
Global variables
 Two ways to create global variables: DEFVAR and
DEFPARAMETER.
 Global variables are conventionally named with names that
start and end with *.
(defvar *count* 0 "Count of widgets made so
far.")
 The difference :
 DEFPARAMETER always assigns the initial value to the
named variable
 DEFVAR does so only if the variable is undefined.
 A DEFVAR form can also be used with no initial value to
define a global variable without giving it a value. Such a
variable is said to be unbound.
Other data structures
 Vectors come in two flavors
 Fixed-size vectors are a lot like arrays in a language such
as Java: a thin veneer over a chunk of contiguous
memory that holds the vector's elements.
 Resizable vectors, on the other hand, are more like
arrays in Perl or Ruby, lists in Python, or the ArrayList
class in Java: they abstract the actual storage, allowing
the vector to grow and shrink as elements are added and
removed.
Vector
 To make fixed-size vectors containing specific values with
the function VECTOR
(vector) #()
(vector 1)  #(1)
(vector 1 2) #(1 2)
 MAKE-ARRAY is more general than VECTOR since you
can use it to create arrays of any dimensionality as well as
both fixed-size and resizable vectors.
 One required argument to MAKE-ARRAY is a list containing
the dimensions of the array.
 (make-array 5 :initial-element nil) #(NIL NIL
NIL NIL NIL)
Vector
 A resizable vector uses fill pointer to store the number
of elements actually stored in the vector.
(make-array 5 :fill-pointer 0)  #()
 To add an element to the end of a resizable vector, use
the function VECTOR-PUSH.
 The function VECTOR-POP returns the most recently
pushed item, decrementing the fill pointer in the
process.
(defparameter *x* (make-array 5 :fill-pointer 0))
(vector-push 'a *x*) 0
*x* #(A)
(vector-push 'b *x*)  1
*x*  #(A B)
(vector-push 'c *x*)  2
*x*  #(A B C)
(vector-pop *x*) C
*x*  #(A B)
(vector-pop *x*)  B
*x* #(A)
(vector-pop *x*) A
*x* #()
Vector
 To make an arbitrarily resizable vector, you need to
pass MAKE-ARRAY another keyword argument:
:adjustable.
(make-array 5 :fill-pointer 0
:adjustable t) #()
 To add elements to an adjustable vector, you use
VECTOR-PUSH-EXTEND, which works just like
VECTOR-PUSH except it will automatically expand
the array if you try to push an element onto a full
vector.
Activity
 Create an array of 5 elements, fill-pointer as 0
 Use a loop to populate the array with 5 numbers
 Then calculate the sum of the numbers
 (defparameter *x* (make-array 5 :fill-pointer 0))
 (defparameter *sum* 0)
 (dotimes (i 5 *x*) (vector-push i *x*))
 (dotimes (i 5 *x*) (setq *sum* (+ *sum* (vector-pop
*x*))))
Lisp input/output
 You can input/output data to:
 standard input/output,
 string or
 file
 A number of functions supported by the Lisp:
 (read) ;; reads the input from the standard input
 (print ‘a) ;; prints to the standard output
 (scanf…) (printf…) (format …) for formatted input and output
 (open ..) (close ..) for opening and closing the files
 (load ..) reads and executes the file
The FORMAT Function
 (format T “Hello”)
 It takes two required arguments:
 A destination for its output: T, NIL, a stream, or a string



T is shorthand for the stream *STANDARD-OUTPUT*
NIL causes FORMAT to generate its output to a string, which it then
returns.
If the destination is a stream, the output is written to the stream.
 A control string that contains literal text and embedded
directives.
 Any additional arguments provide the values used by the
directives in the control string that interpolate values into the
output.
FORMAT Directives
 All directives start with a tilde (~) and end with a
single character that identifies the directive.
 For example, the ~$ directive is used to print floatingpoint values.
(format t "~$" pi)
 Some directives take prefix parameters, which are
written immediately following the tilde, to control
things such as how many digits to print.
(format t "~5$" pi)
 the ~D directive used to output integers in decimal.
 The most general-purpose directive is ~A, which
consumes one format argument of any type and
outputs it in aesthetic (human-readable) form.
 Try the following:
(format nil "The value is: ~a" 10)
(format nil "The value is: ~a" "foo")
(format nil "The value is: ~a" (list 1 2 3))
 ~% emits a newline
 ~& a fresh line
 The difference between the two is that ~% always
emits a newline, while ~& emits one only if it's not
already at the beginning of a line.
Activity
 Suppose you have a list (1 green ribbit) and you need
to print the list like this:
number
color
noise
------ -------- -------1
green
ribbit
 The first column has a width of six; the second & third columns have
widths of eight. The first column is prefixed by three spaces. The
columns are separated by two spaces.
 (setq row (list 1 'green 'ribbit))
 (format t "~& ~6D ~8A ~8A" (first row) (second row)
(third row))
Files and File I/O
 Common Lisp provides a stream abstraction for
reading and writing data and an abstraction, called
pathnames, for manipulating filenames in an
operating system-independent way.
 Functionality unique to Lisp such as the ability to read
and write s-expressions.
Reading File Data
 First, obtain a stream from which you can read a file's
contents with the OPEN function.
(open "/some/file/name.txt")
 OPEN returns a character-based input stream
 Use the object returned as the first argument to any of
the read functions:
 READ-CHAR reads a single character;
 READ-LINE reads a line of text, returning it as a string
with the end-of-line character(s) removed; and
 READ reads a single s-expression, returning a Lisp
object.
(let ((in (open
"/some/file/name.txt")))
(format t "~a~%" (read-line in))
(close in))
 To prohibit error when opening a non-existing file, use
the keyword argument :if-does-not-exist to
specify a different behavior.
 The three possible values are
 :error,
the default;
 :create, which tells it to go ahead and create the file
and then proceed as if it had already existed;
 NIL, which tells it to return NIL instead of a stream.
(let ((in (open
"/some/file/name.txt" :if-doesnot-exist nil)))
(when in
(format t "~a~%" (read-line in))
(close in)))
 The reading functions all take an
optional argument.
 True (default): signal an error if they're
called at the end of the file
 NIL: return the value of their third
argument, which defaults to NIL
(let ((in (open "/some/file/name.txt" :ifdoes-not-exist nil)))
(when in
(loop for line = (read-line in nil)
while line do (format t "~a~%"
line))
(close in)))
File Output
 Need an output stream, which can be obtained by calling OPEN
with a :direction keyword argument of :output.
 OPEN assumes the file shouldn't already exist and will signal an
error if it does.
 Use :if-exists keyword argument to change that:
 :supersede tells OPEN to replace the existing file.
 :append causes OPEN to open the existing file such that new data
will be written at the end of the file.
 :overwrite returns a stream that will overwrite existing data
starting from the beginning of the file.
 NIL will cause OPEN to return NIL instead of a stream if the file
already exists.
(open "/some/file/name.txt" :direction :output
:if-exists :supersede)
File Output
 Data-writing functions:
 WRITE-CHAR writes a single character to the stream.
 WRITE-LINE writes a string followed by a newline, which will be
output as the appropriate end-of-line character or characters for the
platform.
 WRITE-STRING, writes a string without adding any end-of-line
characters.
 Two different functions can print just a newline:
 TERPRI—short for "terminate print"—unconditionally prints a
newline character, and
 FRESH-LINE prints a newline character unless the stream is at the
beginning of a line. FRESH-LINE is handy when you want to avoid
spurious blank lines in textual output generated by different functions
called in sequence.
Closing Files
 It's important to close files when you're done with
them, because file handles tend to be a scarce
resource.
(let ((stream (open "/some/file/name.txt")))
;; do stuff with stream
(close stream) )
 Problems...
 What if you forget to close an opened file?
 What if
close has no chance to execute?
A safer solution in Lisp
 WITH-OPEN-FILE
(with-open-file (stream-var openarguments)
body-forms)
 WITH-OPEN-FILE ensures the stream in stream-var
is closed before the WITH-OPEN-FILE form returns.
Examples:
 To read from a file:
(with-open-file (stream "/some/file/name.txt")
(format t "~a~%" (read-line stream)))
 To create a new file:
(with-open-file (stream
"/some/file/name.txt" :direction :output)
(format stream "Some text."))
Activity
 Find a source program file of Lisp or other languages
on your VM.
 Write a Lisp program to print the content of the source
file.