Programming Languages I LISP
© 1996 by Matthew Smosna June 23, 1996
2
LISP
LISP = LISt Processing language
Motivation: language for Artificial Intelligence – Symbolic computation
– dynamic structures
Precursors:
– IPL: 1950’s (Newell, Simon, Shaw)
» pseudo-code with lists; recursion added later – FPL: 1956
» FORTRAN with lists – LISP: 1960
» John McCarthy
LISP (cont’d)
There are many dialects of LISP!
– Franz Lisp, Portable Standard Lisp (PSL), LISP 1.5, Zlisp, MacLisp, Zetalisp
Two recend (and important) dialects:
– Common Lisp: “Standardized LISP”
» incorporated many features of older LISP’s
» commercial standard (the “Ada” of LISPs)
» big and bulky
– Scheme: 1975 (still being revised)
4
LISP (cont’d)
Most of the discussion is valid for all dialects of LISP.
– Some will be Scheme-specific.
– The programming assignment will be in Scheme.
Every language construct is an expression.
– (f arg1 arg2 .. argn)
Consists of a function and some arguments (may be zero args)
Constants are functions that evaluate to themselves – Like “xy” or 6.
LISP (cont’d)
Every expression returns a value!
– Example: If is a function that takes three arguments:
» (if a b c ) => b if a is true, else c
6
LISP operations
Arithmetic operations:
– (plus x y) or (+ x y ) – (times x y) or (* x y )
In Scheme (or T):
– (add x y) or (+ x y)
LISP operations (cont’d)
Generalized conditional:
– (cond (a1 b1) (a2 b2) ...
(an bn))
Example:
– (cond ((= x y) x) ((< x y) y)
((#t (+ x y)))
For the first ai that evaluates to true, evaluate bi and return that value.
8
Applying functions
Applying functions:
– 8 => 8
– (+ 2 3) => 5
Anonymous functions
Big deal:
– (lambda (x) (+ x x)) => function – ((lambda (x) (+ x x)) 3 ) => 6
These are anonymous functions!
How about named functions?
– Here Scheme is different.
Big deal Param list Function body
10
Definitions
Define variable expression using define – Valid at top level, i.e., not inside anything.
(define x (+ 2 1)) x => 3
(+ x 1) => 4
(define double (lambda (x)
(+ x x))) (double 4) => 8
Definitions (cont’d)
More:
(define fact (lambda (n)
(if (< n 2) 1
(* n (fact (- n 1)))))) (fact 4) => 24
(fact (fact 4)) => 297795584
12
Assignments
Define is a declaration & initialization
– set! is an assignment to a (previously declared) variable.
General format:
– (set! variable expression)
Example:
– (set! a (+ 4 5))
Some implementations permit set! without a define.
Definitions vs. assignments
What’s the difference between a define and a set!? – define is used for definitions; set! is used for
assignments.
– A set! is used to assign to a previously bound
variable; a define is used to create a definition to either a bound or unbound variable; it is an error to perform a set! on an unbound variable.
– The result of a set! is undefined; the result of a define is the variable being bound.
14
Data types
Two basic data types:
– 1. Atoms: numbers, symbols, strings
» Predefined atoms: (), nil, t
» Also: 123, 'a , 'acdbc , ”acbdc”
– 2. Lists: '(a 10 who 6)
» You can also have lists of lists!
» '((a 1) (10 that) 6 9)
LISP programs
LISP programs are represented as lists!!!!
Why?
– Program Data are interchangeable!
Lisp interpreters can easily be implemented in LISP
16
Quiz
Question: How can you specify whether
(f 1 2 3) is a function application or a list?
– Answer: By using the quote function.
– (quote (f A 2 3)) is a list containing the symbols f and A and the numbers 2 and 3.
– (quote B) returns the symbol B.
As a shorthand for (quote (a b c)) use:
– '(a b c)
(Quote e) = 'e
Quiz (cont’d)
Question: What is the result of ... ? (set! a 'b)
(print a)
Answer:
b
18
Quiz (cont’d)
Question: What is the result of:
– (set! a 'b) (print 'a)
Answer:
– a
List operations
Building a list:
– () is the empty list in Lisp (in Scheme:'() )
» '() = NIL
– '(1 2 (a b)) is the list contain 1, 2 and '(a b) – (list 1 2 'a '(b c)) => '(1 2 a (b c))
20
List operations (cont’d)
Adding an element:
– (cons new-element list) => new-list
Adding an element to a list:
– (cons 'a '(1 2 3)) => '(a 1 2 3) – (cons 1 '()) => (1)
– (cons '(a b) '(c d)) => '((a b) c d)
– (cons '(a b) 'c)) => ’((a b) . C)
List operations (cont’d)
cons returns a new list!
– Does not modify its argument ...
– ... but set! does (that’s what the bang indicates).
» (It’s an assignment.)
So,
– (set! a '(1 2 3))
– (set! b (cons 'c a)) – (print b) => (c 1 2 3) – (print a) => (1 2 3)
22
List operations (cont’d)
Accessing elements of a list:
– car: returns the first element of the list.
So,
– (car '(a b c)) => 'a
– (car '((a b) c)) => '(a b) – (car '()) => error
List operations (cont’d)
Another way of accessing elements:
– cdr: returns the rest of the list (everything except the first element).
So,
– (cdr '(a b c)) => '(b c) – (cdr '((a b) (c d))=> '((c d)) – (cdr ()) => error
24
List operations (examples)
So,
– (car (cdr '(a b c))) => 'b – (car (car '((a b) c d) => 'a
– (cdr (car (cdr (cdr '(a b (c d))))))
=> (d)
Some shorthand
Useful combinations of car and cdr have short names:
– (car (cdr x)) => (cadr x) – (car (cdr (cdr x)))=> (caddr x) – (cdr (car (cdr x)))=> (cdadr x)
Basic idea behind the naming convention:
– Take the middle letters of the access functions (car, cdr) and form a single symbol starting with “c” and ending with “r”.
» Not all combinations have a shorthand.
26
Some useful predicates
(null? L) -> returns true if L (L is a list) is ( ).
– (null? '(1)) => false – (null? '()) => true – (null? 6) => false
(symbol? X) -> returns true if X is a symbol.
– (symbol? 6) => false – (symbol? 'x) => true – (symbol? '(a b)) => false
Some useful predicates (cont’d)
(= x y) returns true if x and y are identical.
– Or you can write (eq? x y)
– Represents the same number or symbol or the same list.
For example:
– (eq? 6 6) => true – (eq? 'a 'b) => false
In Scheme, = is used basically for numeric equality, eq?
is used for everything else.
28
Some useful predicates (cont’d)
Consider this (related) sequence:
– (set! x '(1 2 3))
– (eq? x x) => true – (set! y x)
– (eq? x y) => true
But,
– (eq? '(1 2 3) '(1 2 3)) => false – (eq? x '(1 2 3)) => false
True and false
Note: In LISP, true is represented by t (or #t) and false is represented by nil or ‘() or #f.
– (if '() 6 7) => 7
– (= 6 7)=> will return #f !
So, the empty list is also the constant “false” (in Lisp, not in Scheme!).
So,
– (if (cdr '(a)) 6 7) => 7 (6, in Scheme)
Also, anything that is not ( ) or NIL is assumed to be true!
30
Sample function
Sample function: Determine the length of a list:
– (define length (lambda (L) (cond ((null? L) 0)
(t (+ 1 (length (cdr L)))) )))
Sample function (cont’d)
Some shorthand is allowed:
– (define (length L)
(if (null? L) 0
(+ 1 (length (cdr L)))))
The above has an implied lambda.
– There are also forms of define and lambda that are like varargs in C.
32
List representation
How are lists represented?
– “( )” is a constant (some unique value and not a valid address).
A non-empty list is represented by a cons-cell.
CAR CDR Two components:
CAR & CDR
Examples
‘(6)
6 representation
for ( )
or 6
‘(a b c) ‘a
‘b
34
Examples (cont’d)
a
d b
c
‘(a (b c) d)
Examples (cont’d)
a
2 1
(set! a ‘(1 2))
The value of a is a pointer to a cons cell.
36
Examples (cont’d)
cons creates a new cons cell.
– (set! a ‘(a b)) – (set! b ‘(c d))
– (set! c (cons a b))
c
d
‘a
‘b
c
a b
Notice that a and b are unchanged
Examples (cont’d)
This is why eq? is a little strange!
– (eq? x y) will only return true if x and y have the same value:
» Same symbol or ( )
» Same address for a list.
38
List equality
List equality:
– (define (equal x y)
(cond ((atom x) (eq x y)) ((atom y) nil ))
(else (and (equal (car x) (car y)) (equal (cdr x) (cdr y)) ))
Common code sequence;
Base cases: take car and cdr
List equality (cont’d)
Remember:
– (set! x '(1 2 3)) (set! y x)
1
3 2
x y
The result
40
Copying lists
What if we want a copy of a list?
1
3 2
y
1
3 2
x
Copying lists (cont’d)
To copy a list:
– (define (copy x) (if (atom x) x
(cons (car x)) (copy (cdr x)))))
To create a list from elements:
– (list x y z w ... ) =
(cons x (cons y ( ... ) = ”the list” (x y z w ...)
42
An exercise: Flatten
Now some fun!
– Let’s write a function called flatten that removes all internal parentheses
– (flatten '((1 2) 3 (4 5) (6 7 8) (9 10))))
» ... that will yield (1 2 3 4 5 6 7 8 9 10)
– To write this we need the function append removes one pair of parens, i.e.,:
» (append '(...) '(...) ) gives '(... ...)
– Or, put another way, it joins two lists together:
» (append '(1 2 3) '(4 5)) => (1 2 3 4 5)
An exercise (cont’d)
Flatten:
– (define (flatten lis) ((cond
((null? lis) lis) ((atom? (car lis)) (cons
(car lis)
(flatten (cdr lis)))) (t (append (flatten (car lis))
(flatten (cdr lis)))) )))
44
Append
Append: How do you combine the elements of two lists into one list?
– The function append:
» ( append '(a b) '(c d)) => '(a b c d)
– Note that (cons '(a b) '(c d)) => '((a b) cd)
How is append written?
– (define (append x y) (if (null? x)
y
(cons (car x)
(append (cdr x) y))))
See how well recursion works with lists!
Eventually null
Reversing a list
Reverse: (Let’s reverse a list)
– (define (reverse lis) (if (null? lis) nil
(append (reverse (cdr lis)
(list (car lis)) )))
list: creates a list out of a set of elements.
46
Reversing a list (cont’d)
Examples:
– (reverse '( (1 2) (3 4) 5) ) =>
(append (reverse '((3 4) 5)
(lis '(1 2))) =>
(append '(5 (3 4)) ((1 2))) =>
(5 (3 4) (1 2)))
Reversing a list (cont’d)
But this is quadratic complexity!
– To reverse a size n list, call append on sizes 1, ... , n – Complexity = 1 + 2 + ... + n = n2 (approximately)
So, “kiss off” LISP ...
– ... since basic list operations (that is, the LISP stuff) take n2 in LISP and n in Pascal.
But wait ...
48
Reversing a list (cont’d)
Consider:
– (define (reverse lis) (reverse1 lis nil)) – (define (reverse1 f r)
(if (null? f) r
(reverse1 (cdr f)
(cons (car f) r)) )))
Reversing a list (cont’d)
Example:
– reverse '( (1 2) (3 4) 5)) =>
reverse1 '( (1 2) (3 4) 5) nil)=>
reverse1 '( (3 4) 5) '((1 2))) =>
reverse1 '(5) '((3 4) (1 2))) =>
reverse1 nil '(5 (3 4) (1 2)))
50
Side effects
Lists can be modified!
– Side effects!
Example:
– (set-car! L A) == (set (car L) A) – (set-cdr! L M) == (set (cdr L) M)
Not official Scheme
Side effects (cont’d)
Example:
– (set! x '((a b) c d))
d c
x
a
b
52
Side effects (cont’d)
Example:
– (set-car! x '(1 2))
– Called replaca in old LISP.
‘d
‘c x
1
2 a
b
Side effects (cont’d)
Example:
– (set! x '(1 2 3)) – (set-cdr! x '(4 5))
2 x
1 2
x 1 4
54
Side effects (cont’d)
Examples:
– (set! x '(1 2 3))
– (print x) => '(1 2 3) – (set! y x)
– (print y) => '(1 2 3) – (set-car! y ‘8)
– (print x) => '(8 2 3) – (eq? x y) => #t
x is modified.
Quiz
Question: What happens here?
– (set! x '(a b c)) – (set-cdr! x x)
– (print x)
Answer: a a a a a a a a a a a...
56
Quiz (cont’d)
Question: Why?
Answer:
c b
x a
Associative Lists
Assume you have a list of pairs:
– L = ((a b) (x y) ... (q z))
Better L = ( (f1 s1) (f2 s2) ... (fn sn) )
The associative list is a concept that appears in many programing languages (LISP, SETL, Perl, etc.)
– (assoc x L) = first pair where car is x – (assoc 5 ((8 2) (5 4 4) (9 1) (5 1)))
» yields (5 4 4)
58
Associative lists (cont’d)
Example:
– (define grades '((joe 10) (bob 2) (alice 9) (sarah 6) (ted nil) ))
– (assoc sarah grades) => (sarah 6) – (cadr (assoc sarah grades)) => 6 – (assoc tom grades) => #f
Recursion
Recursion is the primary mechanism for repeating actions.
Control structures:
– if, cond, function call
» seen already
– (or x y)
(cond (x t) (t y)) – (and x y)
(cond (x y)
(t nil))
returns value of
first true expression returns value of
first false expression
60
Scheme Loop
Scheme loop.
– Not in all LISPs.
(do ((var1 init1 step1) ...
(vark initk stepk)) (test expression)
commands)
Done if test is false, i.e., body
Value of do
Scheme Loop (cont’d)
Example:
– (do ((x '(10 20 30) (cdr x))
(total 0 (+ total (car x))) ((null? x) total)
)
– Empty body in loop.
– Value is 60.
62
Input/Output
I/O is an important side effect.
– (read): gets one object.
– (write obj): machine readable
– (display obj): human readable
– (read-char), (eof-object? arg), (newline)
Input/output (examples)
Some examples:
==> (define v (read)) 123<cr>
==> v 123
64
Multiple actions
Multiple actions:
– (lambda (x y) (+ x y)) – An old friend.
– (lambda (x y)
(+ x y)
(display x) (display y) ...
)
Also, body of DO loop and others (see the Scheme report)
More on recursion
Using recursion, it is easy to perform an operation on each element of a list.
– (instead of using vectors)
– (define (addl_map a)
(cond ((null? a) ’())
(else (cons (+ 1 (car a))
(addl_map (cdr a)))) ))
Adds 1 to each element of a list (assumes a flat list of numbers).
Question: What is the value returned by (addl_map a)?
66
Function parameters
Do I have to write a different function for each operation I want to perform on the elements of a list?
– No. Pass the operation (function) as a parameter!
(define (mapcar f x)
(cond ((null? x) nil)
(else (cons (f (car x))
(mapcar f (cdr x))) ))
Applies the map (that is, function) f to each car (that is, item) of x.
Applies f to every element of the list X.
Application of function f.
Function parameters (cont’d)
So,
– (mapcar add1 '(1 2 3 4)) => (2 3 4 5)
where (define (add1 x) (+ x 1))
– (mapcar times2 '(3 4 5)) => (6 8 10)
68
Higher order functions
Passing functions as parameters leads to the notion of higher order functions.
– Also known as functionals.
– They are: functions that take other functions as parameters and/or return functions.
We saw that mapcar takes a function as a parameter.
– How about returning a function as the result of an expression???
Higher order functions (cont’d)
Suppose:
– (add1 x) adds 1 to x
– (sub1 x) subtracts 1 from x
Then:
– (define (foo x)
(cond ((= x 1) add1) (else sub1))))
takes a parameter x; if x=1 then return the function add1;
70
Higher order functions (cont’d)
Examples:
– (foo 1) => ”function add1”
– (foo 2) => ”function sub1”
– ((foo 2) 6) => 7 – ((foo 2 18) => 17
– (mapcar (foo3) '(4 5 6)
=> '(3 4 5)
Higher order functions and lambda
What if you wanted to add 3 to every element of a list?
– Well, you could do:
» (define (add3 x) (+ x 3))
» mapcar add3 '(...))
– However, if you are only doing itonce you probably don’t want to bother defining a new function “add3”.
» Instead, write an expression that behaves like the function add3.
72
Another use for lambda
A lambda expression is a “nameless” function.
– (lambda (x) (+ x 3))
This returns a function which takes a parameter x and adds 3 to it.
– (mapcar (lambda (x) (+ x 3)) '(1 2 3))
=> (4 5 6)
– ((lambda (y) (* y z)) 6)
=> 12