360 likes | 622 Views
Symbolic Computation. Lisp and Scheme are interactive languages Numbers, booleans, and symbols Numerals have numbers as their values The booleans #T and #F Symbols, like PETER, must be assigned a value Predicates e.g., NUMBER?, BOOLEAN? Prefix notation and arithmetic operators
E N D
Symbolic Computation • Lisp and Scheme are interactive languages • Numbers, booleans, and symbols • Numerals have numbers as their values • The booleans #T and #F • Symbols, like PETER, must be assigned a value • Predicates • e.g., NUMBER?, BOOLEAN? • Prefix notation and arithmetic operators • Statements have the form (function arg ... arg), e.g., • (+ 2 5) • (NUMBER? 5) • (BOOLEAN? #T)
Symbols & Quotation • Symbols don’t have values unless assigned • 5 5 • peter ? • Sometimes a symbol is just a symbol • 'peter peter • Giving global values using DEFINE • (define peter 'jackson) • peter jackson
Lists & Evaluation • Unless QUOTEd, a list of the form • (S1 S2 S3 ... Sn) is assumed to represent a call to the function S1 with arguments S2, S3, ..., Sn • Scheme does the following: • evaluates S1 by looking up its function definition • evaluates the arguments S2, S3, ..., Sn • applies the function definition to the evaluated arguments • Apart from a number of special forms, e.g., conditional statements, all Scheme expressions have this general form
Scheme Procedures • A Scheme procedure is defined using DEFINE • e.g., a function which takes an argument and adds 1 to it (define increment (lambda (n) (+ n 1))) • Note lack of type declarations among arguments to functions • LAMBDA can be thought of as an abstraction operator • e.g., we turn (+ 10 1) into the increment operation by replacing number to be incremented by a bound variable (lambda (n) (+ n 1))
Lambda Conversion • Function application is lambda conversion • ((lambda (n) (+ n 1)) 10) 11 • Converting the lambda expression means: • replacing variables by the values of arguments in the body of the lambda expression, (+ n 1) becomes (+ 10 1) • evaluating the body in the context of these variable bindings and returning the result (define increment (lambda (n) (+ n 1))) allows us to write • (increment 10) 11
The Importance of Lists • Lists represent both programs and data! • Three basic operations on lists: • CAR returns the head of a list • (car '(a b c)) a • CDR returns the tail of a list • (cdr '(a b c)) (b c) • CONS adds an item to the front of a list • (cons 'a '(b c)) (a b c)
Other List Functions • LIST takes any number of arguments and returns them in a list • E.g., • (list 1 2 3) (1 2 3) • (list 'a 'b 'c 'd) (a b c d) • APPEND copies and joins two lists • E.g., • (append '(a b) '(c d e)) (a b c d e)
Function Composition • You can compose functions in Scheme by embedding the list structures of function calls • E.g., • (+ (+ 1 2) 3) 6 • (* (+ 2 3) (+ 1 3)) 15 • (cons ‘a (cons ‘b ())) (a b) • (append '(a b) (append '(c d) '(e f))) (a b c d e f)
Basic Predicates - NULL? • We need to be able to test if a list is empty • we use NULL? for this, e.g., • (null? '(a b c)) #F • (null? '()) #T • Scheme and LISP often use the empty list, (), to denote falsity, e.g., you may sometimes see • (null? '(a b c)) () • In LISP, the special atom, NIL, also stands for the empty list
Basic Predicates - EQ? • Two expressions are EQ if they evaluate to the same symbols, e.g., • (eq? 'a 'a) #T • (eq? 2 2) #T • (eq? 2 2.0) #F • (eq '(a b) '(a b)) #F • (eq l m) ; after (define l '(a b)) (define m l) #T
Other Equality Tests • EQV? includes booleans and numbers • E.g., • (eqv? 2 2.0) #T • (eqv? '(a b) '(a b)) #F • EQUAL? includes lists also • E.g., • (equal? '(a b) '(a b)) #T
The Conditional: COND • The conditional expression has this structure: (cond (test1action1) (test2action2) ... (else default)) (cond ((null? x) NIL) ((number? x) x) (else (list x)))
A Simple Conditional: IF • You can use IF for a simple if-then-else test • E.g., (if (number? x) x (list x)) • IFs can be nested, but then a COND may be better • E.g., (if (null? x) NIL (if (number? x) x (list x)))
Tail (or Flat) Recursion - APPEND • Here is a definition of APPEND, using CONS: (define append (lambda (l m) (if (null? l) m (cons (car l) (append (cdr l) m))))) • Note indentation, which makes code more readable
More Tail Recursion - REMOVE • Here is a definition of REMOVE, using CONS (define remove (lambda (x l) (cond ((null? l) NIL) ((eq? x (car l)) (remove x (cdr l))) (else (cons (car l) (remove x (cdr l))))))) • Note use of COND for case analysis
Inefficiencies - Naive Reverse • Here is a naive definition of REVERSE (define reverse (lambda (l) (if (null? l) NIL (append (reverse (cdr l)) (list (car l)))))) • APPEND copies its arguments & CDRs through its first argument, consuming both time and space
Accumulators - Better REVERSE • A definition of REVERSE that avoids APPEND: (define reverse (lambda (l) (rev-help l NIL))) (define rev-help (lambda (l result) (if (null? l) result (rev-help (cdr l) (cons (car l) result)))))
Lists & Trees • Correspondence between lists and trees • E.g., ((B C) A (D (E F) G)) can be thought of as a tree: A G D B C E F
Depth-First Tree Search • Searching sublists before siblings (define search (lambda (x l) (cond ((null? l) NIL) ((atom? (car l)) (or (eq? x (car l)) (search x (cdr l)))) (else (or (search x (car l)) (search x (cdr l)))))))
Breadth-First Tree Search • Searching siblings before sublists • (define search • (lambda (x l) • (cond ((null? l) NIL) • ((atom? (car l)) • (or (eq? x (car l)) (search x (cdr l)))) • (else (or (search x (cdr l)) • (search x (car l)))))))
Another Tree Recursion Example • Counting the number of atoms in an embedded list • (define count • (lambda (l) • (cond ((null? l) 0) • ((atom? (car l)) • (add1 (count (cdr l)))) • (else (+ (count (car l)) • (count (cdr l)))))))
Final Tree Example - Flatten • FLATTEN takes internal parentheses out of a list • (define flatten • (lambda (l) • (cond ((null? l) NIL) • ((atom? (car l)) • (cons (car l) (flatten (cdr l)))) • (else (append (flatten (car l)) • (flatten (cdr l)))))))
Procedures As Arguments • The function (FILTER predlist) removes all elements from list which do not satisfy pred (define filter (lambda (pred l) (cond ((null? l) NIL) ((atom? (car l)) (if (pred (car l)) (cons (car l) (filter pred (cdr l))))) (else (append (filter pred (car l)) (filter pred (cdr l)))))))
Iteration with Mapping Functions • (MAP functionlist) applies function to every member in list and returns the result in a list: • E.g., • (map add1 '(1 2 3)) (2 3 4) • (APPLY functionlist) applies function to list: • E.g., • (apply max '(3 100 45)) 100 • Note that function names are not quoted
Procedures As Values • Curried functions: Definition of ADDN (define addn (lambda (x) (lambda (y) (+ x y)))) • (ADDN X) returns a function which always adds X to its argument • ((addn 5) 10) 15
Lexical Scoping • Definition of ADDN works because: • All free variables in a lambda expression are assigned values at the time that the lambda is defined (i.e., evaluated and returned) • If a free variable has no value, an error is generated • Bound variables do not receive values until the lambda is applied (i.e., converted) • This convention is called lexical scoping • In the opposite convention, called dynamic scoping, free variables have values determined when lambda is applied • Failure to find a value at run-time generates an error • Early versions of LISP were dynamically scoped, but Common LISP and Scheme are lexically scoped
Binding Environments (I) • Declaring local variables with LET (let ((var1 val1) (var2 val2) ... (varN valN)) body) • Equivalent to the following LAMBDA application ((lambda (var1 var2 ... varN) body) val1 val2 ... valN)
Binding Environments (II) • LETREC allows bindings to reference each other (letrec ((even? (lambda (x) (or (zero? x) (odd? (sub1 x)))) (odd? (lambda (x) (and (not (zero? x)) (even? (sub1 x))))) (odd? 17)) • This is an example of mutual recursion
Why We Need LETREC • This LET statement would generate an error (let ((x 5) (y (+ x 1))) (* x y)) because it translates into a LAMBDA application ... ((lambda (x y) (* x y)) 5 (+ x 1)) where ‘x’ is unbound outside of the LAMBDA
Procedural Abstraction • Here is an abstract version of tail recursion (define tail-recur (lambda (seed action) (letrec ((helper (lambda (l) (if (null? l) seed (action (car l) (helper (cdr l))))))) helper)))
Using Procedural Abstraction • Defining summation function for lists (define sum (tail-recur 0 +)) • This is equivalent to: (define sum (lambda (l) (if (null? l) 0 (+ (car l) (sum (cdr l)))))))
Abstraction of Depth-First Search • With success criteria & successor function • (define search • (lambda (stack next success) • (cond ((null? stack) NIL) • ((success (car stack)) (car stack)) • (else (search • (append (next (car stack)) (cdr stack)) • next • success)))))
Abstraction of Breadth-First Search (define search (lambda (stack next success) (cond ((null? stack) NIL) ((success (car stack)) (car stack)) (else (search (append (cdr stack) (next (car stack))) next success)))))
Better Function Using LETREC (define search (lambda (stack next success) (letrec ((search-help (lambda (l) (cond ((null? l) NIL) ((success (car l)) (car l)) (else (search-help (append l (next (car l)))))))) (search-help stack))))
Managing Local State (define make-light (lambda () (let ((color 'red)) (lambda (msg) (case msg (show color) (change (if (eq? color 'red) (set! color 'green) (set! color 'red))) (else (writeln “Message not known”))))))
Creating objects & sending messages (define alight (make-light)) <PROCEDURE> (alight 'show) RED (alight 'change) GREEN (alight 'show) GREEN