CS 242 Topics Data Abstraction and Modularity � Modular program development • Step-wise refinement • Interface, specification, and implementation � Language support for modularity John Mitchell • Procedural abstraction • Abstract data types – Representation independence – Datatype induction • Packages and modules • Generic abstractions – Functions and modules with type parameters Stepwise Refinement Dijkstra’s Example (1969) � Wirth, 1971 begin • “… program ... gradually developed in a sequence of print first 1000 primes refinement steps” end begin • In each step, instructions … are decomposed into variable table p more detailed instructions. fill table p with first 1000 primes � Historical reading on web (CS242 Reading page) print table p begin • N. Wirth, Program development by stepwise end int array p[1:1000] refinement, Communications of the ACM, 1971 make for k from 1 to 1000 • D. Parnas, On the criteria to be used in decomposing p[k] equal to k-th prime systems into modules, Comm ACM, 1972 print p[k] for k from 1 to 1000 • Both ACM Classics of the Month end Program Structure Data Refinement � Wirth, 1971 again: Main Program • As tasks are refined, so the data may have to be refined, decomposed, or structured, and it is natural to refine program and data specifications in parallel Sub-program Sub-program Sub-program Sub-program Sub-program 1
Example Modular program design � Top-down design Bank Transactions • Begin with main tasks, successively refine � Bottom-up design • Implement basic concepts, then combine Deposit Withdraw Print Statement � Prototyping • Build coarse approximation of entire system � For level 2, represent account • Successively add functionality Print transaction balance by integer variable history � For level 3, need to maintain list of past transactions Modularity: Basic Concepts Example: Function Component � Component � Component • Meaningful program unit • Function to compute square root – Function, data structure, module, … � Interface � Interface • float sqroot (float x) • Types and operations defined within a component � Specification that are visible outside the component • If x> 1, then sqrt(x)* sqrt(x) ≈ x. � Specification � Implementation • Intended behavior of component, expressed as float sqroot (float x){ property observable through interface float y = x/2; float step= x/4; int i; � Implementation for (i= 0; i< 20; i+ + ){ if ((y* y)< x) y= y+ step; else y= y -step; step = step/2;} return y; • Data structures and functions inside component } Example: Data Type Heap sort using library data structure � Priority queue: structure with three operations � Component empty : pq • Priority queue: data structure that returns elements insert : elt * pq → pq in order of decreasing priority deletemax : pq → elt * pq � Interface � Algorithm using priority queue (heap sort) • Type pq begin • Operations empty : pq empty pq s insert : elt * pq → pq deletemax : pq → elt * pq insert each element from array into s remove elements in decreasing order and place in array � Specification end • Insert add to set of stored elements This gives us an O(n log n) sorting algorithm (see HW) • Deletemax returns max elt and p q of remaining elts 2
Language support for info hiding Abstract Data Types � Procedural abstraction � Prominent language development of 1970’s • Hide functionality in procedure or function � Main ideas: � Data abstraction • Separate interface from implementation – Example: • Hide decision about representation of data structure • Sets have empty, insert, union, is_member?, … and implementation of operations • Sets implemented as … linked list … • Example: priority queue can be binary search tree or • Use type checking to enforce separation partially -sorted array – Client program only has access to operations in interface – Implementation encapsulated inside ADT construct In procedural languages, refine a procedure or data type by rewriting it. Incremental reuse later with objects. Origin of Abstract Data Types Comparison with built-in types � Structured programming, data refinement � Example: int • Write program assuming some desired operations • Can declare variables of this type x: int • Later implement those operations • Specific set of built-in operations + , -, * , … • Example: • No other operations can be applied to integer values – Write expression parser assuming a symbol table � Similar properties desired for abstract types – Later implement symbol table data structure • Can declare variables x : abstract_type � Research on extensible languages • Define a set of operations (give interface) • What are essential properties of built -in types? • Language guarantees that only these operations can • Try to provide equivalent user-defined types be applied to values of abstract_type • Example: – ML sufficient to define list type that is same as built-in lists Clu Clusters ML Abstype complex = cluster is � Declare new type with values and operations make_complex, real_part, imaginary_part, plus, times abstype t = < tag> of < type> rep = struct [ re, im : real] with make_complex = proc (x,y : real) returns ( cvt) val < pattern> = < body> return (rep${ re:x, im:y} ) ... real_part = proc ( z:cvt) returns real fun f(< pattern> ) = < body> ... return (z.re) end imaginary_part = proc (z:cvt) returns real return (z.im) � Representation plus = proc (z, w: cvt) returns (cvt ) t = < tag> of < type> similar to ML datatype decl return (rep${ re: z.re+ w.re, im: z.im+ w.im } ) mult = proc … end complex 3
Abstype for Complex Numbers Abstype for finite sets � Input � Declaration abstype cmplx = C of real * real with abstype 'a set = SET of 'a list with fun cmplx(x,y: real) = C(x,y) val empty = SET(nil) fun x_coord(C(x,y)) = x fun insert(x, SET(elts)) = ... fun y_coord(C(x,y)) = y fun union(SET(elts1), Set(elts2)) = ... fun add(C(x1,y1), C(x2,y2)) = C(x1+ x2, y1+ y2) fun isMember(x, SET(elts)) = ... end end � Types (compiler output) � Types (compiler output) type cmplx type 'a set val cmplx = fn : real * real -> cmplx val empty = - : 'a set val x_coord = fn : cmplx -> real val insert = fn : 'a * ('a set) -> ('a set) val y_coord = fn : cmplx -> real val union = fn : ('a set) * ('a set) -> ('a set) val add = fn : cmplx * cmplx -> cmplx val isMember = fn : 'a * ('a set) -> bool Encapsulation Principles Representation Independence � Integers � Representation Independence • Can represent 0,1,2, … , -1,-2, … any way you want • Elements of abstract type can be implemented in • As long as operations work properly various ways + , -, * , /, print, … • Restricted interface -> client program cannot • Example distinguish one good implementation from another 1’s complement vs. 2’s complement � Datatype Induction � Finite Sets • Method for reasoning about abstract data types • can represent finite set { x, y, z, … } any way you want • As long as operations work properly • Relies on separation between interface and empty, ismember?, insert, union implementation • Example linked list vs binary tree vs bit vector Reality or Ideal? Induction (Toward Datatype Induction) � In Clu, ML, … rep independence is a theorem � Main idea • Can be proved because language restricts access to • 0 is a natural number implementation: access through interface only • if x is a natural number, then x+ 1 is a natural number � In C, C+ + , this is an ideal • these are all the natural numbers � Prove p(n) for all n • “Good programming style” will support representation independence • prove p(0) • The language does not enforce it • prove that if p(x) then p(x+ 1) Example: print bit representation of -1 • that’s all you need to do This distinguishes 1’s complement from 2’s complement Skip: Will not cover datatype induction in any depth this year 4
Recommend
More recommend