Title: Functional programming: LISP
1Functional programming LISP
- Originally developed for symbolic computing
- Main motivation include recursion (see McCarthy
biographical excerpt on web site). - First interactive, interpreted language
- Dynamic typing values have types, variables do
not - Garbage-collected
- Ability to form program fragments and execute.
- Extensible multiple dialects, implementations.
Convenient testbed for language design
experiments - Main descendants Scheme (compact) Common Lisp
(massive). Statically typed functional languages
ML
2Uniform syntax lists
- Expressions are either atoms or lists
- atoms are numeric or symbols
- lists nest, to form full trees
- Syntax is simple because programmer supplies what
would otherwise be the internal representation of
a program - ( ( 10 12) ( 7 11)) evaluates
(1012 711) - A program is a list
- (define (factorial n) (if (eq n 0) 1
- ( n
(factorial (- n 1))))
3List manipulation
- Three primitives and one constant
- get head of list car
- get rest of list cdr
- add an element to a list cons
- null list nil or ()
- Add equality ( or eq) and recursion, and this
is a universal model of computation
4Rule of evaluation
- A number evaluates to itself
- An atom evaluates to its current binding
- A list is a computation.
- Its first element must evaluate to an operation
- the remaining elements are actual parameters
- the result is the application of the operation to
the evaluated actuals
5Quoted data
- If every list is a computation, how do we
describe data? - Another primitive quote
- (quote (1 2 3 4))
- gt (1 2 3 4)
- (quote (this is a simple declarative
sentence) - gt (this is a simple declarative
sentence) - (this also works)
- gt (this also works)
6Decomposing a list
- (car (this is a list of symbols))
- gt this
- (cdr (this is a list of symbols))
- gt (is a list of symbols)
- (cdr (this that))
- gt (that) a list
- (cdr (singleton))
- gt () the empty list
- (car ()) run time error
7Building lists
- (cons this (that and the other))
- gt (this that and the other)
- (cons a ())
- gt (a)
- useful shortcut list
- (list a b c d e)
- gt (a b c d e)
- equivalent to
- (cons a (cons b (cons c (cons d (cons
e ())))))
8Control structures
- Conditional
- (if condition expr1 expr2)
- Generalized form
- (cond
- (pred1 expr1)
- (pred2 expr2)
-
- (else exprn)
- Needs special rule evaluate only the successful
entry - if and cond are not regular functions
9Function declarations
- (define (sqr n) ( n n))
- define is also special body is not evaluated
- defines produces a binding sqr is bound to the
body of the computation - (lambda (n) ( n n))
- define can produce value bindings as well
- (define x 15)
- (sqr x)
- gt 225
10Lambda or no lambda
- (define (sqr n) ( n n))
- And
- (define sqr (lambda (n) ( n n)))
- Mean the same thing. The advantage of the lambda
formulation is one can have nameless functions,
e.g. - ((lambda (x y) ( x y)) 5 6)
- The lambda brackets the formal parameters.
11Eval
- McCarthy had the idea what if I could formulate
some data and then execute it - (eval ( 5 7))
- (eval '(define x 15)) creates the binding (x
15)
12Recursion
- (define (add1 x) ( x 1)) the
beginnings of Peano arithmetic - (define (sub1 x) (- x 1))
- (define (add x y)
- (if ( y 0) x
- (add ( (add1 x) (sub1 y))))
- (define (times x y)
- (cond
- (( y 0) 0)
- (( y 1) x)
- (else (add x (times (x (sub1 y)))))))
13Recursion (ii)
- (define (exp x y)
- (cond
- ((eq y 0) 1)
- (else (times x (exp x (sub1 y))))))
- better
- (define (fast-exp x y)
- (cond ( y 0) 1)
- ( (even? y) (square (fast-exp x (/
y 2)))) - (else ( x (fast-exp x (- y 1))))))
- (define (even? n) ( (remainder n 2) 0))
defining a predicate
14Recursion on lists
- (define (member elmt lis)
- (cond
- ((null? lis) ())
- ((eq elmt (car lis)) lis)
- (else (member elmt (cdr lis)))))
- convention return rest of list, starting from
elmt, rather than t or f - convention every non-false value is true in a
boolean context
15Predicates
- If variables are untyped, need run-time tests to
determine kind - symbol?
- number?
- list?
- null?
- zero?
- Syntax conventions differ in different dialects
symbolp, numberp, listp, zerop...
16Functional arguments(functional a function
that takes functions as arguments)
- (define (map fun lis)
- (cond
- ((null? lis) ())
- (cons (fun (car lis)) (map fun (cdr
lis))))) - (map sqr (map sqr ( 1 2 3 4))
- gt (1 16 81 256)
17Environments
- An environment describes the current bindings of
symbols - A binding is a pair (symbol value)
- A frame is a list of bindings (activation
record), i.e. an association list ((s1 v1) (s2
v2)) - An environment is a list of frames
- In some cases we can treat the environment as a
single association list (e.g. with dynamic
binding)
18Procedures and their environment
- A function has three components a list of
formals, a body, and environment of definition.
Formals and body are represented by a lambda
expression - (lambda (f1 f2 ..) expr)
- A function is evaluated in its environment of
definition (lexical scoping) after adding the
current bindings of the actuals (activation
record) - The definition must capture all three components
- (define (make-procedure spec body env)
- (list procedure (make-lambda spec
body) env)) - make-procedure is called by eval to evaluate a
define
19Scoping Rules
- Consider the following examples
- (define x 5)
- (define sqr (lambda (z) ( z z )))
- (sqr x) yields 25
- (define addto (lambda (y) (define x 6) ( x y)))
- (addto 9) yields 15 given definition of x as 6
- (sqr x) what should this give?
20Scoping Rules 2
- Because Scheme is lexically scoped, the
answer is still 25. The scope of the definition
of x within addto is just that function. - Lexical scoping scope of variable is defined by
text of program. - A dynamically scoped Lisp variant would
simply take the last definition of x.
21Now Consider this
- (define x 5)
- (define sqr (lambda (z) ( z z )))
- (sqr x) yields 25
- (define x 8)
- (sqr x)
22 x has been redefined in the same scope as in its
first definition, so x changes
23This notion naturally extends to functions(look
up closure in Scott)
- (define (compose f1 f2)
- (lambda (x) (f1 (f2 x))))
- (define (sqr x) ( x x))
- (define fourth (lambda (x)
- ((compose sqr sqr) x)))
- (fourth 4) yields 256
- When fourth is called, we need to fetch the
latest definition of sqr at the right level of
scoping.
24What happens here?
- (define (compose f1 f2)
- (lambda (x) (f1 (f2 x))))
- (define (sqr x) ( x x))
- (define fourth (lambda (x)
- ((compose sqr sqr) x)))
- (define (sqr x) ( x x))
- (fourth 4) yields 16 (based on latest def
of sqr at
proper scope level
25And here?
- (define (compose f1 f2)
- (lambda (x) (f1 (f2 x))))
- (define (sqr x) ( x x))
- (define fourth (lambda (x)
- ((compose sqr sqr) x)))
- (define (foo x) (define (sqr x) ( x x))
- ( 2 (sqr x)))
- (foo 15) yields 60
- (fourth 4) yields 256
26Evaluating a function definition
- (define (name binding) (car binding))
- results in the binding
-
- (name
- (procedure (lambda (binding) (car
binding)) env)) - procedure and lambda are labels to indicate kind
- Env is the current environment. At the top level
it includes the binding for car (used in the
body) as well as all other predefined symbols.
27Dynamic binding
- Alternative definition for free variables use
most recent binding for that symbol. Useful in
cases such as - (define (sum fun a next b)
- (if ( gt a b) 0
- ( (fun a) (sum fun (next a)
b)))) - (define sum-powers a b n)
- (sum nth-power a incr b)
- could write (sum
nth-power (lambda (x) ( x 1)) b) - (define (nth-power x) (expt x n))
Which N? - n undefined if static binding.
- Bound in sum-powers if dynamic
binding.
28Implementing dynamic binding
- Change eval to pass current environment to apply
- change apply to use current environment rather
than procedure environment - Eval ... (else (apply
- (eval (car exp) env)
apply function - (eval-list (cdr exp) env)
to arguments - env))
in current env - Apply (eval (procedure-body procedure)
- (extend-environment
add bindings - (parameters
procedure) of formals - arguments
- env)))
to current env
29Other binding constructs
- Local variables
- (let
- ((x 5) (y 10))
x and y bound simultaneously - ( x y))
- (let
- ((x 5) (y (expt x 2))
sequential bindings y after x - ( x y 3))
30Binding formals to actuals
- (define (make-frame formals actuals)
- (cond
- (null? formals) ()) should
check (null? actuals) also - ( else (cons
- (add-assoc (car formals) (car
actuals)) - (make-frame (cdr
formals) (cdr actuals)))))) - (define (extend-environment formals actuals
env) - (cons (make-frame formals actuals) env)
31Self-definition (advanced)
- (define (eval exp env) the lisp
interpreter - (cond
- ((number? exp) exp)
numbers are self-evaluating - ((symbol? exp) (lookup exp env))
a symbol has a binding - ((null? exp) ())
- ((eq (car exp) quote) (car (cdr
exp))) could write cadr - ((eq (car exp) car) (car (car
(cdr exp)))) caadr - ((eq (car exp) cdr) (cdr (car
(cdr exp)))) cdadr - (else (apply (eval (car exp) env)
apply function -
(eval-list (cdr exp) env))) to arguments
32Function application defined (advanced)
- (define (apply procedure arguments)
- (eval (procedure-body procedure)
- (extend-environment
- (parameters procedure)
- arguments
- (environment procedure))))
- In words add actuals to environment, evaluate
body of procedure in new environment - Note environment is in procedure definition
(closure)
33Association lists
- (define (add-assoc symb val env)
- (cons (list symb val) env))
add a binding - (define (name binding) (car binding)) for
readability - (define (value binding) (car (cdr binding)))
- (define (lookup symb env)
sequential search - (cond
- ((null? env) ())
error detected later - ((eq symb (name (car env))) (value (car
env)) - (else (lookup symb (cdr env)))))
34Procedure components
- given the representation
- (procedure (lambda parameters
body) env) - the components of a procedure can be obtained as
follows - (define (parameters proc) (cadr
(cadr proc))) - (define (procedure-body proc)
(caddr (cadr proc))) - (define (environment proc) (caddr
proc))
35Tail recursion
- Tail recursive function needs single stack frame.
mandated by semantics of Scheme - (define (factorial n) (if (zero? N) 1)
- ( n (factorial (- n 1)))
stack grows to size n - define factorial n) (fact-iter 1 1 n)
alternate definition - (define (fact-iter prod count var)
- (if (gt count var) prod
- (fact-iter ( count prod)
tail recursion - ( count 1)
implemented as loop - var))))
36Implementation of tail recursion
- If last operation in function is recursive call,
overwrite actuals and go to beginning of code - (define (last lis)
- (if (null? (cdr lis) (car lis))
- (last (cdr lis)))) can
be done with loop - (define (length lis)
- (if (null? lis) 0)
- ( 1 (length (cdr lis)))) not tail
recursive!