470 likes | 654 Views
PPL. Pairs, lists and data abstraction. Data Abstraction?. An interface: separate implementation from usage Think of the Map interface in Java: we know how to use it, but we don’t know how it’s implemented . Motivation: better software design, modularity, less maintenance and more!.
E N D
PPL Pairs, lists and data abstraction
Data Abstraction? • An interface: separate implementation from usage • Think of the Map interface in Java: we know how to use it, but we don’t know how it’s implemented. • Motivation: better software design, modularity, less maintenance and more!
Data Abstractions in Scheme? • Of course, but first we need to present 2 new data types in Scheme: • Pairs • Lists
Pairs • Combines two data entities into a single unit • Scheme provides built in primitives: • Value constructor: cons • Selectors: car, cdr • Identify predicate: pair? • Equality: equal?
Pairs > (define x (cons 1 2)) > (car x) 1 > (cdr x) 2 > x (1 . 2) “toString” of pair
Pairs Each data entity can be anything! > (define y (cons x (quote a))) > (car y) (1 . 2) > (cdr y) 'a > y ((1 . 2) . a) Recall that x is the pair (cons 1 2)
Pairs Do not confuse (cons 1 2) with (1 . 2) !! 1st is a Scheme expression (syntax) and 2nd is the ‘toString’ of the value
Pairs Type constructor: PAIR [T1*T2 -> PAIR(T1,T2)] (λ (x y) (cons x y))
Recursive Pairs (define rec-pairs (λ (n) (if (= n 1) 1 (cons n (rec-pairs (- n 1)))))) > (rec-pairs 5) (5 4 3 2 . 1) ;(cons 5 (cons 4 (cons 3 (cons 2 (cons 1)))))
Recursive Pairs We can also represent binary trees! (cons (cons 1 2) (cons (cons 3 4) (cons 5 6))) ((1 . 2) (3 . 4) 5 . 6)
Recursive Pairs • We have trees! How about searching a tree? (define member ? (lambda (el pair) (cond ((and (pair? (car pair)) (member? el (car pair))) #t) ((eq? el (car pair)) #t) ((and (pair? (cdr pair)) (member? el (cdr pair))) #t) ((eq? el (cdr pair)) #t) (else #f))))
Recursive Pairs • How about counting number of leaves? (define count-leaves (λ (t) (if (not (pair? t)) 1 (+ (count-leaves (car t)) (count-leaves (cdr t))))))
Lists • Finite sequence<v1, …, vn>(v1 … vn) • v1 is head, (v2 … vn) is tail • Value constructors: cons and list • Empty: (list) • Non-empty: • (cons head tail) – tail must be a list! • (list e1 … en)
Lists • Both will create the same list: (list 1 2 3) (cons 1 (cons 2 (cons 3 (list))))
Lists • Selectors: • car: head • cdr: tail • Predicates: • list? • null? • equal?
Lists and Types • Homogenous • Examples: (1 2 3), ((1 2) (3)) • LIST(Number), LIST(T), … • Heterogeneous • Examples: (1 #f 3), ((1 2) 3) • LIST
Useful List Operations car + cdr: > (define x (list 5 6 8 2)) > (car x) 5 > (cdr x) (6 8 2) > (car (cdr x)) 6 > (cadr x) 6 > (cddr x) (8 2)
Selector: list-ref nth element of a list: (define list-ref (λ (ln) (if (= n 0) (car l) (list-ref (cdr l) (- n 1))))) (define squares (list 1 4 9 16 25 36)) (list-ref squares 4)
Operator: length (define length (λ (l) (if (null? l) 0 (+ 1 (length (cdr l)))))) (define squares (list 1 4 9 16 25 36)) (length squares) 6
Iterative length (define length (λ (l) (letrec ((iter (λ (l c) (if (null? l) c (iter (cdr l) (+ c 1)))))) (iter l 0))))
Operator: append (define append (λ (l1 l2) (if (null? l1) l2 (cons (car l1) (append (cdr l1) l2))))) (append (list 1 2 3) (list 3 4))
Iterative append ;; Type: [LIST * LIST -> LIST] (define append (λ (l1 l2) (letrec ((iter (λ (l1 l2 c) (if (null? l1) (c l2) (iter (cdr l1) l2 (λ (app-cdr-l1) (c (cons (car l1) app-cdr-l1)))))))) (iter l1 l2 (lambda (x) x)))))
Constructor make-list Builds a list of given with given values (define make-list (λ (l v) (if (= l 0) (list) (cons v (make-list (- l 1) v)))))
Using Lists to Represent Trees • Very much like pairs, but better: • Empty tree () • Leaf is just value • Non-empty tree – non-empty list • Example: (1 (2 3)) is the tree:
Using Lists to Represent Trees • How can we add data to non-leaf nodes? (i.e. labeled tree) : each node is also a list! • ((1) ((2) (3))) • Now we can create labled trees: • (1 (0) (3 (2) (4)))
Leaves Count • Unlabeled tree (define count-leaves (λ (t) (cond ((null? t) 0) ((not (list? t)) 1) (else (+ (count-leaves (car t)) (count-leaves (cdr t)))))))
Type Correctness with Pairs and Lists • cons and list are primitives (not special forms!) • So we need more axioms.
Pairs For every type environment _Tenvand type expressions _S,_S1,_S2: Tenv|- cons:[S1*S2 -> PAIR(S1,S2)] Tenv|- car:[PAIR(S1,S2) -> S1] Tenv|- cdr:[PAIR(S1,S2) -> S2] Tenv|- pair?:[S -> Boolean] Tenv|- equal?:[PAIR(S1,S2)*PAIR(S3,S4) -> Boolean]
Homogenous Lists For every type environment Tenv and type expression S: Tenv|- list:[Empty -> LIST(S)] Tenv|- list:[S* ...*S -> LIST(S)] n >0 Tenv|- cons:[S*LIST(S) -> LIST(S)] Tenv|- car:[LIST(S) -> S] Tenv|- cdr:[LIST(S) -> LIST(S)] Tenv|- null?:[LIST(S) -> Boolean] Tenv|- list?:[S -> Boolean] Tenv|- equal?:[LIST(S)*LIST(S) -> Boolean]
Heterogeneous Lists For every type environment Tenv and type expression S: Tenv|- list:[Empty -> LIST] Tenv|- cons:[S*LIST -> LIST] Tenv|- car:[LIST -> S] Tenv|- cdr:[LIST -> LIST] Tenv|- null?:[LIST -> Boolean] Tenv|- list?:[S -> Boolean] Tenv|- equal?:[LIST*LIST -> Boolean]
What About Data Abstraction? • An interface: separation between usage (client) and implementation (supplier) • Supplier gives getters, setters and other operators, and client uses them. • Order of development: • Client level • Supplier level
ADT • It’s a new type with a difference: type is semantic while ADT is syntactic • Signature of constructor • Signature of operators • Invariants (see later)
So Why start with Pairs and Lists? • Pairs and lists will be our implementation! The client will not know we used pairs and lists, she will just use the operations we give her! • Example next slide
Binary Tree ADT: Constructors Signature: make-binary-tree(l,r) Purpose: Returns a binary tree whose left sub-tree is l and whose right sub-tree is r Type: [Binary-Tree*Binary-Tree -> Binary-Tree] Pre-condition: binary-tree?(l) and binary-tree?(r) Signature: make-leaf(d) Purpose: Returns a leaf binary-tree whose data element is d Type: [T -> Binary-Tree]
Binary Tree ADT: Selectors Signature: left-tree(r), right-tree(r) Purpose: (left-tree <t>): Returns the left sub-tree of the binary-tree <t>. (right-tree <t>): Returns the right sub-tree of the binary-tree <t>. Type: [Binary-Tree -> Binary-Tree] Pre-condition: composite-binary-tree?(t) Signature: leaf-data(r) Purpose: Returns the data element of the leaf binary-tree <t>. Type: [Binary-Tree -> T] Pre-condition: leaf?(t)
Binary Tree ADT: Predicates Signature: leaf?(t) Type: [T -> Boolean] Post-condition: true if t is a leaf -- constructed by make-leaf Signature: composite-binary-tree?(t) Type: [T -> Boolean] Post-condition: true if t is a composite binary-tree -- constructed by make-binary-tree Signature: binary-tree?(t) Type: [T -> Boolean] Post-condition: result = (leaf?(t) or composite-binary-tree?(t) ) Signature: equal-binary-tree?(t1, t2) Type: [Binary-Tree*Binary-Tree -> Boolean]
Binary Tree ADT: Invariants leaf-data(make-leaf(d)) = d left-tree(make-binary-tree(l,r)) = l right-tree(make-binary-tree(l,r)) = r leaf?(make-leaf(d)) = true leaf?(make-binary-tree(l,r)) = false composite-binary-tree?(make-binary-tree(l,r)) = true composite-binary-tree?(make-leaf(d)) = false
Binary Tree ADT: Client Level ;Signature: count-leaves(tree) ;Purpose: Count the number of leaves of ’tree’ ;Type: [binary-Tree -> number] (define count-leaves (lambda (tree) (if (composite-binary-tree? tree) (+ (count-leaves (left-tree tree)) (count-leaves (right-tree tree))) 1)))
Binary Tree ADT: Implementation ;Signature: make-binary-tree(l,r) ;Type: [(LIST union T1)*(LIST union T2) ; -> LIST] ;Pre-condition: binary-tree?(l) ; and binary-tree?(r) (define make-binary-tree (lambda (l r) (list l r))) ;Signature: make-leaf(d) ;Type: [T -> T] (define make-leaf (lambda (d) d)) ;Signature: left-tree(t) ;Type: [LIST -> LIST union T] ;Pre-condition: composite-binary-tree?(t) (define left-tree (lambda (t) (car t))) ;Signature: right-tree(t) ;Type: [LIST -> LIST union T] ;Pre-condition: composite-binary-tree?(t) (define right-tree (lambda (t) (cadr t)))
Binary Tree ADT: Implementation ;Signarture: leaf-data(t) ;Type: [T -> T] ;Pre-condition: leaf?(t) (define leaf-data (lambda (t) t)) ;Signarture: leaf?(t) ;Type: [T -> Boolean] (define leaf? (lambda (t) #t)) ;Signarture: composite-binary-tree?(t) ;Type: [T -> Boolean] (define composite-binary-tree? (lambda (t) (and (list? t) (list? (cdr t)) (null? (cddr t)) (binary-tree? (left-tree t)) (binary-tree? (right-tree t)))
Binary Tree ADT: Invariants Seems ok, but look: > (leaf? (make-leaf (list 5 6))) #t > (has-leaf? (list 5 6) (make-leaf (list 5 6))) #f We have no way to distinct composite leaves from tree… We need a better implementation: tagged-data!
Tagged Data ADT Signature: attach-tag(x,tag) Purpose: Construct a tagged-data value Type: [T*Symbol -> Tagged-data(T)] Signature: get-tag(tagged) Purpose: Select the tag from a tagged-data value Type: [Tagged-data(T) -> Symbol] Signature: get-content(tagged) Purpose: Select the data from a tagged-data value Type: [Tagged-data(T) -> T] Signature: tagged-data?(datum) Purpose: Identify tagged-data values Type: [T -> Boolean] Signature: tagged-by? (tagged,tag) Purpose: Identify tagged-data values Type: [T*Symbol -> Boolean]
Binary Tree ADT: Implementation using Tagged-Data ;Signature: make-binary-tree(l,r) ;Type: [(LIST union T1)*(LIST union T2) ; -> Tagged-data(LIST)] ;Pre-condition: binary-tree?(l) ; and binary-tree?(r) (define make-binary-tree (lambda (l r) (attach-tag (list l r) ’composite-binary-tree))) ;Signature: make-leaf(d) ;Type: [T -> Tagged-data(T)] (define make-leaf (lambda (d) (attach-tag d ’leaf))) ;Signature: left-tree(t) ;Type: [Tagged-data(LIST) ; -> Tagged-data(LIST union T)] ;Pre-condition: composite-binary-tree?(t) (define left-tree (lambda (t) (car (get-content t)))) ;Signature: right-tree(t) ;Type: [Tagged-data(LIST) -> Tagged-data(LIST union T)] ;Pre-condition: composite-binary-tree?(t) (define right-tree (lambda (t) (cadr (get-content t))))
Tagged-Data Implementation • Have you noticed that we didn’t implement the tagged-data ADT? • That’s the whole idea! We are clients! We don’t have to know the implementation! • But we’ll give it anyway…
Tagged-Data ADT Implementation ;Signature: attach-tag(x,tag) ;Type: [Symbol*T -> PAIR(Symbol, T)] (define attach-tag (λ (x tag) (cons tag x))) ;Signature: get-tag(tagged) ;Type: PAIR(Symbol,T) -> Symbol (define get-tag (λ (tagged) (car tagged))) ;Signature: get-content(tagged) ;Type: [PAIR(Symbol,T) -> T] (define get-content (λ (tagged) (cdr tagged))) ;Signature: tagged-data?(datum) ;Type: [T -> Boolean] (define tagged-data? (λ (datum) (and (pair? datum) (symbol? (car datum))))) ;Signature: tagged-by?(tagged,tag) ;Type: [T*Symbol -> Boolean] (define tagged-by? (λ (tagged tag) (and (tagged-data? tagged) (eq? (get-tag tagged) tag))))