PS5

advertisement
‫עקרונות שפות תוכנות‬
1122 '‫סמסטר ב‬
:5 ‫תרגול‬
Compound Data and Data Abstraction
Topics
1. Type Pair
2. Type List
3. Data Abstraction (ADTs, Pair-based and List-Based implementations).
Type Pair
Pair is a Type implemented in Scheme, representing an ordered pair of values (of any type).
A pair(<a1>.<a2>) is constructed by applying cons over <a1> and <a2>.
a pair's type is PAIR(T1,T2).
The Pair type is implemented as part of Scheme (primitive).
Pair –Operations:
cons: Create a Pair.
car: Get the Pair’s first element
cdr: Get the Pair’s second element
Type:T1 * T2 -->PAIR(T1, T2)
Type: PAIR(T1, T2) --> T1
Type: PAIR(T1, T2) --> T2
pair?:check if an expression is a Pair
Type: T --> Boolean
equal?: check the equality of two Pairs Type: T1 * T2 --> Boolean
Examples (The box-pointer drawing is for visualization only)
> (cons 1 2)
(1 . 2) This is the printing form of pairs.
1
2
> (cons 1 (cons 2 (lambda () (+ 1 2))))
(1 2 . #<procedure>)
1
2
1
#<procedure>
> (cons (car (cons 1 2)) (cdr (cons 3 4)))
(1 . 4)
>(define c (cons 1 (cons 'cat (cons 3 (cons 4 5)))))
>c
(1 cat 3 4 . 5)
> (cdddr c)
(4 . 5)
> (cons (cons 1 2) (cons 1 3))
((1 . 2) 1 . 3)
1 2
1 3
Type LIST
List is a Type implemented in Scheme, representing an ordered collection of values (of any type).
A sequence <a1>, <a2>, ...<an> is constructed by repeated applications of the cons starting from
the empty list: (cons <a1> (cons <a2> (cons ... (cons <an> (list)) ...))).
a list's type is either LIST if it is heterogeneous or LIST(T) for homogeneous lists with elements all of
type T.
List – Operations:
list: Creates a list.
(list): Creates null (an empty list).
cons: Creates a list when used correctly.
Type: T*T*…*T->List(T)
Type: List
Type: T*List(T)->List(T)
car: Gets the list’s first element.
cdr: Gets the list without the first element.
Type: List(T)->T
Type: List(T)->List(T)
list?: Checks if an expression is a list.
null?: Check if an expression is null (an empty list).
equal?: Checks equality of lists.
Type: T->Boolean
Type: List(T)->Boolean
Type: List(T)*List(T)->Boolean
Other operations: append, length and many more.
Examples
>(define L1 (list))
>(null? L1)
#t
>(length L1)
0
>(define L2 (list 1 (list 2 3) 6))
2
>L2
(1 (2 3) 6)
> (car L2)
1
> (caadr L2)
2
> (cadadr L2)
3
>(cdddr L2)
()
> (list? (cons 2 (cons (list 2 3) 4)))
#f
> (list (list 1 2) 1 2)
1
1
2
2
Question – Draw the box-pointer diagram of the list below, representing a Scheme
expression:
(list ‘cond (list (list ‘= ‘n ‘0) ‘0)
(list (list ‘= ‘n ‘1) ‘1)
(list ‘else (list ‘+ (list ‘fib (list ‘- ‘n ‘1))
(list ‘fib (list ‘- ‘n ‘2)))))
Example – filter
Signature: filter(predicate, sequence)
Purpose: return a list of all sequence elements that satisfy the predicate
Type: [[T-> Boolean]*LIST(T) -> LIST(T)]
(define filter
(lambda (predicate sequence)
(cond ((null? sequence) (list))
((predicate (car sequence))
(cons (car sequence)
(filter predicate (cdr sequence))))
(else (filter predicate (cdr sequence))))))
>(filter pair?
(list (cons 'a 'b)'c (cons 'd (cons 'e 'f)) 'g))
( (a. b) (d . (e . f)) )
3
Data Abstraction
Data abstraction is a methodology that separates compound data usage (the Client/Concept Level)
from compound data implementation(Supplier/Concrete Level), using an Abstract Data Type barrier.
An ADT is a specification that includes:

o
o
An interface (A set of Contracts for a value constructor, various operators, an
identifier), and
 Invariants that describe the behavior/inter-dependence of the ADTs
procedures.
Client programs use ADTs without knowledge about the implementation. The decision which
implementation is used should not change the code of the Client.
An implementation is developed independently of the client. An implementation is a fulfillment of
the ADT’s contract, which respects the ADTs invariants. There may be several implementations for a
single ADT, varying in features, like efficiency, inner-representation, etc.
Example 1 - The Circle ADT:
Goal: Design an abstract data type for manipulating circles on a plane.
Circle ADT Definition
Desired (Client) actions: Move a circle, Get the area of a circle.
1. Interface (Data operations' specification):
;;; 1. Signature: make-circle(x-center y-center radius)
;;; 2. Purpose: constructs a circle.
;;; 3. Type: Client view: [Number* Number* Number -> Circle ]
;;; 4. Pre-conditions: radius >= 0.
;;; 1. Signature: get-x-center(circle)
;;; 2. Purpose: returns the x coordinate of the circle's center.
;;; 3. Type: Client view:[ Circle -> Number]
;;; 1. Signature: get-y-center(circle)
;;; 2. Purpose: returns the y coordinate of the circle's center.
;;; 3. Type: Client view:[ Circle -> Number]
;;; 1. Signature: get-radius(circle)
;;; 2. Purpose: returns the radius of the circle.
;;; 3. Type: Client view: [Circle -> Number]
;;; 1. Signature: circle? (x)
;;; 2. Purpose: returns true iff x is a circle.
;;; 3. Type: [T -> Boolean]
4
;;; 1. Signature: circ-equal?(circ1 circ2)
;;;2. Purpose: Compare two circles.
;;;3.Type: Client view: [Circle * Circle -> Boolean]
NOTE: Circle is an abstract type: It is not supported by the language Scheme or by the Type
specification grammar. It is user-defined and is known only to the programmers.
Therefore, the client programs can refer to ADT Circle, but the implementation of Circle
must use the types that Scheme supports (or other ADTs).
2. Invariants:
1. For all x, y, r it holds:
(get-x-center (make-circle x yr)) = x
(get-y-center (make-circle x yr)) = y
(get-radius (make-circle x yr)) = r
2. For every e (circle? e) = #t iff there exist x,y,r s.t. e is the value of (make-circle x y r).
3. For every e1 and e2 (circ-equal? e1 e2) = #t iff there exist x,y,r s.t. both e1 and e2
are the value of (make-circle x y r).
Usage –Client Side
The client knows only the above specification and so it is all the client needs in order to work with
circles.
;;; 1. Signature: move-circle(circ x y )
;;; 2. Purpose: return a circle, with a center point that is transposed by (x,y).
;;; 3. Type: Client view: [Circle*Number*Number -> Circle]
(define move-circle (lambda (circ x y)
(make-circle (+ (get-x-center circ) x)
(+ (get-y-center circ) y)
(get-radius circ))))
;;; 1. Signature: area-circle(circ)
;;; 2. Purpose: Calculate the area of circ
;;; 3. Type: Client view: [Circle -> Number]
(define area-circle (lambda (circ)
(let((pi 3.1459))
(* pi (square (get-radius circ))))))
5
An Implementation of Circle ADT Using Pairs:
We will represent our ADT as a pair of pairs:
'circle
radius
x-center y-center
The label 'circle' in front of every circle implementation is our only way to distinguish circles from other
pairs. It serves like a type-tag in syntactically typed languages.
;;;Type: Supplier view: [Number* Number* Number
 Pair(Symbol,Pair(Number,Pair(Number,Number))) ]
(define make-circle (lambda (x-center y-center radius)
(cons 'circle (cons radius (cons x-center y-center)))))
NOTE: Here it is important to specify the concrete type, recognized by Scheme, while
the abstract type is not a Scheme type. So, in this implementation
Circle =Pair(Symbol, Pair(Number, Pair(Number, Number)))
The following three selectors have the same type,
;;;Type: Supplier view: [ Pair(Symbol,Pair(Number,Pair(Number,Number))) -> Number]
;;; Pre-condition: (circle? circ)-->#t
(define get-x-center (lambda (circ) (car (cdr (cdr circ)))))(Or caddr)
(define get-y-center (lambda (circ) (cdddr circ)))
(define get-radius (lambda (circ) (cadr circ)))
;;;Type: Supplier view: [ T -> Boolean]
(define circle? (lambda (circ)
(and (pair? circ) (eq? (car circ) 'circle))))
;;;Type: Supplier view: [Pair(Symbol, Pair(Number, Pair(Number, Number)))
* Pair(Symbol, Pair(Number, Pair(Number, Number))) Boolean]
;;; Pre-condition: (circle? circ1) and (circ? circ2)-->#t
(define circ-equal? (lambda (circ1 circ2)
(and (=(get-x-center circ1) (get-x-center circ2))
(=(get-y-center circ1)(get-y-center circ2))
6
(= (get-radius circ1) (get-radius circ2)))))
Proving the invariants of an ADT
The set of procedures must fulfill the interface and respect the invariants of the ADT, for it to be
considered an implementation of the ADT.
We prove the 3rd rule for the get-radius selector: We show that for every x, y and r it holds that
applicative-eval [(get-radius (make-circle xyr))] = r.
(This is a sketch of running the applicative-eval algorithm:)
applicative-eval[ (get-radius (make-circle x y r)) ] =>*
applicative-eval[ (get-radius ('circle . (r .(x . y )))) ] ==>*
applicative-eval[ (car (cdr ('circle . (r .(x . y ))))) ] ==>*
applicative-eval[ (car (r .( x . y ))) ] ==>* r.
Example 2 - Symbolic Differentiation:
This problem was, historically, one of the motivating problems for the development of
computer languages for symbolic manipulation. It led to the current powerful systems for
symbolic mathematical manipulations.
Derivation without symbolic manipulations
(if time permits)
First, we will show how to compute the derivative of a function at a specific point. Here is a
function which receives a function f and a size delta dx, and returns a function which when given
point x, returns the derivative of function f in that point.
Signature: deriv(f dx)
Purpose: to construct a procedure that computes the derivative dx approximation of a function:
deriv(f dx)(x) = (f(x+dx) - f(x) )/ dx
Type: [Number -> Number] * Number -> [Number -> Number]
Pre-conditions: dx < 1
Post-condition: result=closure r, s.t. (r x) = (/ (- (f (+ x dx)) (f x)) dx)
(define deriv
(lambda (f dx)
(lambda (x)
(/ (- (f (+ x dx)) (f x))
dx))))
PROBLEM: The above derivation function gives us the value of derivative of a function only
at a certain point. It's more useful and accurate to find an actual expression for the
derivative, if possibly. For a polynomial function this is easy to do the use of symbols.
7
Goal: Write a procedure for differentiation of expressions, built with additions and
multiplications, with at most two arguments. For example:
2
The derivation of ax + bx + c , by x, where a, b, c are numbers is 2ax + b.
Note that the expressions are handled here as DATA, not as PROCEDURES.
Example:
𝑑
(𝑥(𝑦 + 5))
𝑑𝑥
> (derive (make-product (make-variable 'x)
(make-sum (make-variable 'y)
(make-constant 5))
(make-variable 'x)))
(+ y 5)
The derivation rules are:
o
o
o
o
dc
 0 , if c is a constant or a variable different from x.
dx
dx
1.
dx
d(u  v) du dv

 .
dx
dx dx
d(u  v)
du
dv
 v u
dx
dx
dx
Looking into the derivation task we see that we need the following types of algebraic
expressions: **** variable, constant, sum, product ****
These types will have, each, constructors and selectors. For example, the SUM type needs a
constructor for "making" a sum, and selecting its elements.
The Algebraic Expression (AExpression) ADT:
We present here the interface specification, without the invariants.
CONSTANT:
(make-constant <e>)
(constant? <e>)
VARIABLE:
(make-variable <e>)
(variable? <e>)
(same-variable? <v1><v2>)
SUM:
(make-sum <e1><e2>)
(sum? <e> )
8
(augend<e>)
(addend<e>)
PRODUCT:
(make-product <e1><e2>)
(product? <e>)
(multiplier<e>)
(multiplicand<e>)
Usage – Client Side
;;; Signature: derive(exp var)
;;; Purpose: find the derivative of an algebraic expression according to a variable
;;; Type: [AExpression * AExpression ->AExpression]
;;; Pre-condition (variable? var)
(define derive (lambda (exp var)
(cond ((constant? exp) (make-constant 0))
((variable? exp)
(if (same-variable? exp var) (make-constant 1)
(make-constant 0)))
((sum? exp)
(make-sum (derive (augend exp) var)
(derive (addend exp) var)))
((product? exp)
(make-sum
(make-product (multiplier exp)
(derive (multiplicand exp) var))
(make-product (derive (multiplier exp) var)
(multiplicand exp))))
(else
(error "unknown expression type -- DERIVE" exp)))))
Implementing the algebraic expression ADT using lists
We choose the implement algebraic expressions as lists, written in prefix notation.
For example, ax+b would be represented as the list: (+ (* a x) b). The symbols '+ , '* serve as
type tags.
The implementation defines AExpression = Number union Symbol union List.
Lists are used for composite data (sum, product) where the operation symbols '+ and '*
tag the data.
;;; Type: Number -> Number
(define make-constant (lambda (x) x ))
(define constant? (lambda(x) (number? x)))
9
;;; Type: Symbol -> Symbol
(define make-variable (lambda(x) x))
;;; Type: T-> Boolean
(define variable? (lambda(x) (symbol? x)))
;;; Type: Symbol* Symbol-> Boolean
;;; Pre-conditions: (variable? v1) and (variable? v2)
(define same-variable? (lambda(v1 v2) (eq? v1 v2)))
;;; Type: (Number union Symbol union List)*(Number union Symbol union List) -> List
(define make-sum (lambda(a1 a2)
(cond ((and (constant? a1) (constant? a2))
(make-constant (+ a1 a2)))
((and (constant? a1) (= a1 0)) a2)
((and (constant? a2) (= a2 0)) a1)
(else (list '+ a1 a2)))))
;;; Type: T -> List
(define sum? (lambda(x) (and (list? x) (equal? (car x) '+))))
;;; Type: List -> T
(define augend (lambda(s) (cadr s)))
(define addend (lambda(s) (caddr s)))
;;; Type: T*T -> List
(define make-product (lambda(m1 m2)
(cond ((and (constant? m1) (constant? m2))
(make-constant (* m1 m2)))
((and (constant? m1) (= m1 0))
(make-constant 0))
((and (constant? m1) (= m1 1)) m2)
((and (constant? m2) (= m2 0))
(make-constant 0))
((and (constant? m2) (= m2 1)) m1)
(else (list '* m1 m2)))))
;;; Type: T -> Boolean
(define product? (lambda(x) (and (list? x)(eq? (car x) '*))))
;;; Type: List -> T
(define multiplier (lambda(p) (cadr p)))
;;; Type: List -> T
(define multiplicand (lambda(p) (caddr p)))
10
> (derive (make-sum (make-variable 'x) (make-constant 6))
(make-variable 'x))
1
𝑑
(𝑥(𝑦 + 5))
𝑑𝑦
> (derive (make-product (make-variable 'x)
(make-sum (make-variable 'y)
(make-constant 5)))
(make-variable 'y))
x
11
Download