Principles of Programming Languages - 2007Spring by yrs83496

VIEWS: 3 PAGES: 12

									• PracticalSessions » DataAbstraction

Practical Session #4 – Simple Program Verification; Data
Abstraction


Part 1 – Simple Program Verification
Relevant book section: Paulson 6.3

We will show that the requirements for (several parts of) the contract are met.
We will deal with:

    1) Type correctness
    2) Post conditions hold if pre-conditions hold

Type Correctness

Both parameters and the returned value should adhere to the types specified within
the contract. This is done with Type Inference. The basic method is:

    1) For parameters - to examine how they are used inside the procedure. If they
       are passed to a procedure whose argument types are known (such as the
       primitive +, or a procedure for which we already verified the types), we may
       assume they are of the correct type.
    2) For the returned value – basically the same method. If we return an atomic
       expression, its type is the returned type. If we apply another procedure (and
       return the value of its application as the result), the returned type is that of the
       procedure.

N.B: Complete correction proofs for complicated functions are beyond the scope of
this course.

Example 1 - recursive Fibonacci series calculation:

;;; 1. Signature: (fib n)

;;; 2. Type: Number -> Number

;;; 3. Purpose: to compute the nth Fibonacci number.

;;; 4. Example: (fib 5) should produce 5

;;; 5. Pre-conditions: n >= 0,

;;; 6. Post-conditions: result = nth Fibonacci number.

                                             1
;;; 7. Tests: (fib 3) expected value 2

;;;     (fib 1) expected value 1

(define (fib n)

  (cond ((= n 0) 0)

            ((= n 1) 1)

            (else (+ (fib (- n 1))

                  (fib (- n 2))))))

Proof of type correctness:

       1. The parameter n is an argument to the procedures '=' and ‘-‘, and is therefore a
          Number.
       2. We apply the function '+' in order to get the returned value, so our returned
          value must be a Number.

       Remark: In Scheme the type of a variable is not explicitly defined when the
       variable is defined, it can only be inferred.

Example 2 – The Function move-left-up

;;; 1. Signature: move-left-up(f n)

;;; 2. Type: [Number -> Number]*Number -> [Number -> Number]

 ;;; 3. Purpose: to create a function which is similar to f, but its graph is moved by n
units to the left and up

;;; 4. Example: (move-left-up square 3) should return g(x) s.t. g(x)=((x+3)^2)+3.

;;;                ((move-left-up square 3) 4) should return (4+3)^2+3 = 52

 ;;; 5. Pre-conditions: f is defined for all x. n is a natural number (this is relied upon in
the post –condition induction based proof).

 ;;; 6. Post-conditions: result=g(x)=n+f(x+n). g's values are similar to f's but are
shifted left and up by n units (same for the graphs).

;;; 7. Tests: 1. ((move-left-up square 3) 4) value: 52, as expected

;;;;             2. ((move-left-up square 4) 2) value: 40, as expected

;;;; 8. definition

                                              2
(define (move-left-up f n)

    (lambda (x) (+ n (f (+ x n))))

)

Proof of type correctness:

      1. The parameter f is applied to the result of a summation, and yields an
         argument for ‘+’, so is a function Number->Number.
      2. n is an argument to ‘+’ so it is a number.
      3. The returned value is the value of the 'lambda' special operator, which is a
         closure. The parameter to this closure is x which is an argument to ‘+’, so
         it is a number. The returned value is the result of a summation, and is
         therefore a number.

Post conditions

Here we prove by induction (assuming the pre-conditions are met):

      1) Base – when the function is called with values resulting in no recursive calls.
      2) Hypothesis – assume the post-conditions hold for every integer in [0,n]. (n is
         natural)
      3) Step – prove that the requirements are still met for n+1.

      1. Typically, the only non-trivial step is #3. Expand the call f(…, n+1, …) by
         definition, and try to achieve an expression containing the call to f(..., n, …),
         which may be replaced under the Hypothesis.

Example – fast exponentiation

;;; 1. Signature: fast-exp(b, n)

;;; 2. Type: Number -> Number

 ;;; 3. Purpose: To compute the exponent n of a natural number b in O(log2(n)) steps,
according to the following rules:

;;;             1. n=0  expt = 1;

                 2. n is even expt(b,n) = square(expt(b, n/2))

                 3. n is oddexpt(b,n) = n*expt(b, n-1)

;;; 4. Example: fast-exp(2,5) should return 32

;;; 5. Pre-conditions: n is a non-negative integer. If n is zero, b must not be zero.


                                              3
;;; 6. Post-conditions: result=b^n.

;;; 7. Tests:

 (define (fast-exp b n)

  (cond ((= n 0) 1)

       ((even? n) (square (fast-exp b (/ n 2))))

       (else (* b (fast-exp b (- n 1))))))

Inductive proof of post-conditions: (we prove by complete induction, which is a
variant of the basic mathematical induction—when correctness for n+1 stems not
only from correctness for n, but from the correctness for 1..n )

   1) Base: n=0 result = 1 = b^0, for every b<>0.

                Also, for n=1 result = b *fast-exp(b, 0) = b*1 = b = b^n

   2) Hypothesis: assume for values in [0,n] the condition holds
   3) Inductive step:
         a. n is even n+1 is odd. Fast-expt(b, n+1) = b*fast-expt(b, n) = b*b^n
             = b^(n+1)
         b. n is odd  n+1 is even. Fast-expt(b, n+1) = square(fast-
             expt(b,(n+1)/2)). We may assume n>1 (we already proved the claim
             for n<=1), and thus (n+1)/2<n  square(fast-expt(b,(n+1)/2)) =
             square(b^[(n+1)/2]) = b^(n+1) as required.




Part 2 – Data Abstraction
Relevant book section SICP Section 2.1

Concepts

       Procedure abstraction:
           o Separate the way the procedure would be used from the details of
              how the procedure is implemented in terms of hopefully simpler
              procedures.
       Data abstraction:
           o A methodology that enables us to isolate how a compound
              data object is used from the details of how it is constructed
              from more primitive data objects.




                                             4
Data abstraction:

      Client programs use "abstract data". Make no unnecessary (internal)
       assumptions about the data that are not strictly necessary for
       performing the task at hand.

      At the same time, a "concrete" data representation (or
       implementation) is defined independent of the programs that use the
       data.
           o There are many ways to implement a single concrete data
               type
           o It shouldn't change the code of the "abstract" (client)
               program.

      The interface between concrete and abstract data representations is
       a set of procedures, called selectors and constructors that implement
       the abstract data in terms of the concrete representation.

      Correctness rules determines the correctness of various
       implementations (that still can vary by other features, like efficiency,
       stability, etc.).

      The operations of an ADT are like a “family”: There implementations
       depend on each other.


Example: Geometric Operations for Circles

Design a set of procedures for manipulating circles on x-y plane. Suppose we
want to do geometric calculations with circles.

Desired (high-level) actions:

      Move a circle

      Scale a circle

      Get the area of a circle

      Compare circles

      Check if circles intersect

      Add circles (create a different circle)



                                          5
Circle Abstract Data-structure (Type ADT) can be represented by the
components:

      x-center

      y-center
      radius

Interface (selectors, constructors)

[to be implemented later…]
      (make-circle ) returns the circle.
      (get-x-center ) returns the x-center of the circle .
      (get-y-center ) returns the y-center of the circle
      (get-radius ) returns the radius of the circle



Definition of abstract data type Circle:

 1. ; move circ center point by x on x-axis and by y on y-axis
 2. ; [Circle, Number, Number -> Circle]
 3. (define (move-circle circ x y)
 4.    (make-circle (+ (get-x-center circ) x)
 5.              (+ (get-y-center circ) y)
 6.              (get-radius circ)))
 7.
 8. ; scale circ by scale
 9. ; [Circle, Number -> Circle]
 10. (define (scale-circle circ scale)
 11.     (make-circle (get-x-center circ)
 12.              (get-y-center circ)
 13.              (* (get-radius circ) scale)))
 14.
 15. ; calculate the area of circ
 16. ; [Circle -> Number]
 17. (define (area-circle circ)
 18.      (let ((pi 3.14))
 19.        (* pi (square (get-radius circ)))))

                                                  6
20.
21. ; compare two circles
22. ; [Circle, Circle -> Boolean]
23. (define (equal-circles? circ1 circ2)
24.     (and (=(get-x-center circ1) (get-x-center circ2))
25.             (=(get-y-center circ1) (get-y-center circ2))
26.             (=(get-radius circ1) (get-radius circ2))))
27. ; auxiliary procedure – distance
28. ; [Circle, Circle -> Number]
29. (define (distance-circles circ1 circ2)
30.     (let ((x-diff (- (get-x-center circ1) (get-x-center circ2)))
31.              (y-diff (- (get-y-center circ1) (get-y-center circ2)))
32.      (let
33.             ((center-dist (sqrt (+ (square x-diff) (square y-diff)))))
34.       center-dist)))
35.
36. ; check if circles intersect
37. ; they intersect if the distance between the center of the two
38. ; circles is smaller that the sum of the radiuses of the circles.
39. ; [Circle, Circle -> Boolean]
40. (define (intersect-circles? circ1 circ2)
41.     (let ((center-dist (distance-circles circ1 circ2))
42.             (sum-radiuses (+ (get-radius circ1) (get-radius circ2))))
43.      (let
44.             ((answer (> sum-radiuses center-dist)))
45.        answer)))
46.
47. ; add circles. Return a circle with an average center and some of radiuses of both circles
48. ; [Circle, Circle -> Circle]
49. (define (add-circles circ1 circ2)
50.     (let ((new-x-center (average (get-x-center circ1)
51.                            (get-x-center circ2)))
52.             (new-y-center (average (get-y-center circ1)
53.                            (get-y-center circ2)))
54.             (new-radius (+ (get-radius circ1)
55.                       (get-radius circ2))))
56.      (make-circle new-x-center new-y-center new-radius)))

                                                    7
  The operations (move-circle, etc) are defined in terms of data objects
  (radius, x, y, circle)

Circle "Concrete data" representation

  How can we tell that the "concrete data" representation is a good one?

  The constructor and selectors must satisfy specified conditions that make
  the representation valid. for every real numbers x-center, y-center and
  radius:
   1.        (get-x-center (make-circle x-center y-center radius)) = x-center

   2.        (get-y-center (make-circle x-center y-center radius)) = y-center
   3.        (get-radius (make-circle x-center y-center radius)) = radius

  Correctness rules:
            Describe everything that is expected from the behavior of any
             “concrete data” implementing the ADT’s interface.
            Describe the relationship and interactions between the results of
             combinations of consecutive calls to the different procedures in the
             ADT’s interface.

Implementation #1 (procedural "eager" style)

  constructor:

             General remark: Some contracts should be added to the procedures in this ps.

             ; [Number, Number, Number -> [{0, 1, 2} -> Number] ]

   1.        (define (make-circle x-center y-center radius)

   2.         (let ((dispatch (lambda (m)

   3.          (cond ((= m 0) x-center)

   4.                  ((= m 1) y-center)

   5.                  ((= m 2) radius)

   6.                 (else (error "Illegal argument – make-circ " m))))))

   7.            dispatch))

  selectors:

        1.   ; [ [{0, 1, 2} -> Number] -> Number]

   2.        (define (get-x-center circ) (circ 0))
                                                     8
 3.    ; [ [{0, 1, 2} -> Number] -> Number]

 4.    (define (get-y-center circ) (circ 1))

 5.    ; [ [{0, 1, 2} -> Number] -> Number]

 6.    (define (get-radius circ) (circ 2))

Example: evaluation steps in the substation model (applicative order) for
(get-radius (make-circle 10 11 12))

       (This is a sketch of running the EVAL algorithm:)

       1.    1.   (get-radius (make-circle 10 11 12))

 2.    2.     (get-radius

 3.         (lambda (m)

 4.                (cond ((= m 0) 10)

 5.                         ((= m 1) 11)

 6.                         ((= m 2) 12)

 7.                   (else

 8.                  (error " Illegal argument - make-circ " m)))) )

 9.    3.    ((lambda (m)

 10.               (cond ((= m 0) 10)

 11.                        ((= m 1) 11)

 12.                        ((= m 2) 12)

 13.                        (else

 14.                         (error " Illegal argument - make-circ " m))))

 15.                                 2)

 16.   4.    (cond ((= 2 0) 10)

 17.                 ((= 2 1) 11)

 18.                 ((= 2 2) 12)

 19.                (else

 20.                 (error " Illegal argument - make-circ " 2)))

 21.   5.    12




                                               9
  Notes:
           The value returned by make-circ is a procedure – dispatch
           Validity of correctness rules: The selector, for example (get-
     y-center circ), applies circ to 1. If circ is the procedure formed by
     (make-circ x y z), then circ applied to 1 will yield y.

           High-order procedures provide the ability to represent
     compound data.




Implementation #2 (procedural "lazy" style)

  This is a different procedural representation of circles.
  selectors:

           1.    ;;; Type: [ // get-x-center

           2.    ; [ // circ

           3.    ;      [ Number, Number, Number -> Number]

           4.    ; -> Number]

           5.    ; -> Number ]

           6.    (define (get-x-center circ)

           7.        (circ (lambda (a0 a1 a2) a0)))

           8.    ; in the lambda: return the first parameter

           9.

           10. ; [ [ [ Number, Number, Number -> Number] -> Number] -> Number ]

           11. (define (get-y-center circ)

           12.       (circ (lambda (a0 a1 a2) a1)))

           13.

           14. ; [ [ [ Number, Number, Number -> Number] -> Number] -> Number ]

           15. (define (get-radius circ)

           16.       (circ (lambda (a0 a1 a2) a2)))




                                                  10
   Constructor:

            1.      ; [Number, Number, Number

    2.       ; -> [

    3.       ;        [ Number, Number, Number -> Number]

    4.       ;      -> Number]

    5.       ;]

    6.       (define (make-circle x-center y-center radius)

    7.           (lambda (m) (m x-center y-center radius)))

   Difference between lazy style and eager style procedure implementation:

            In lazy style, most the logic/complexity is in the selectors,
         whereas in eager style implementation, it is in the constructor.
            Efficiency of computation: The eager style prepares everything
         in the construction of data values. Selection is immediate. The lazy
         style – does nothing in construction time. Selection is more
         expensive.



Question: Would you that the ADT “family” in eager style is closed (
already defined data values cannot have new selectors), while in lazy
style, the family is open for new members?
Answer: both implementations require some changes when adding new
members, although the lazy implementation is more "open", since it may
be possible to add new selectors without altering the constructor (as long
as the "data" doesn’t change)


   Check Validity of this representation:
   Example: (get-y-center (make-circ 10 11 12))=11 evaluation steps:

             1.     (get-y-center (make-circle 10 11 12))

             2.     (get-y-center (lambda (m) (m 10 11 12)))

             3.     ( (lambda (m) (m 10 11 12))    (lambda (a0 a1 a2) a1)) )

             4.     ( (lambda (a0 a1 a2) a1)) 10 11 12 )

             5.     11

                                                  11
   Summary – Abstraction levels:

(draw the abstraction diagram)
          Client Programs that use circles…
          Abstract data move-circle, equal-circles?…

          Interface impl., concrete: make-circle, get-x-center, get-radius …
          Implementation level uses procedures(eager/lazy…)




                                       12

								
To top