Building Abstractions with Data (Part 1)

Post on 24-Feb-2016

45 views 0 download

description

Building Abstractions with Data (Part 1). CS 21a: Introduction to Computing I First Semester, 2013-2014. Last Time…. Procedural Abstraction Creating layers of abstraction for algorithms. Today…. Data Abstraction Creating layers of abstraction for data. Outline. The Notion of Types - PowerPoint PPT Presentation

Transcript of Building Abstractions with Data (Part 1)

Building Abstractions

with Data (Part 1)

CS 21a: Introduction to Computing I

First Semester, 2013-2014

Last Time…►Procedural Abstraction►Creating layers of abstraction for

algorithms

Today…►Data Abstraction►Creating layers of abstraction for data

Outline►The Notion of Types►Abstractions with Data Types►Same Type, Different Implementations

The Notion of Types►“G” is a letter, but not a number.►“800-9000” is a telephone number,

but “name@example.com” is an e-mail address.

►What you’re sitting on is a chair, not a table (hopefully).

►You are a person, not a dog (don’t let your significant other change that).

The Notion of Types►Any value has a type.►Any piece of data has a data type.►Why is this important?

Type Safety►There are procedures that can only be

meaningfully carried out if the input data is of a certain type.►Examples:

►Sitting should be done on chairs, (maybe occasionally on tables if you have good reasons), but not on candles.

►Squaring should be done on numbers, but not on words.

Type Safety►Helps catch programming errors early►No accidental squaring of words and

using the result as a prescribed amount of medicine.

Which Assignments Are Allowed?

int a = 16;

double b = 18.5;

a = b;

double c = 18;

a = c;

c = a;

Type Safety in Variablesint a = 16;

double b = 18.5;

a = b; // not allowed

double c = 18; // allowed: integers are real numbers

a = c; // still not allowed

c = a; // allowed: integers are real numbers

Type Safety in Variablesint a = 16;

double b = 18.5;

a = b; // not allowed

double c = 18; // allowed: integers are real numbers

a = c; // still not allowed

c = a; // allowed: integers are real numbers

Type specifiers put a restriction on what values a variable can hold.

Type Safety in Proceduresstatic int square(int x){

return x*x;} parameter type specifier

return type specifier

return value must be of correct type

Will This Work?int a = square("Hello");

Will This Work?int a = square("Hello");

square expects integer arguments. Design by contract, and propagate type safety down the barriers of abstraction: square needs to ensure that it’s given an integer, because it will use that argument in a * operation, which also requires its operands to be numbers. The user of square must therefore follow this restriction and only pass an integer argument.

How About This?int a = square(3);

How About This?int a = square(3.0);

How About This?double x = 3;int a = square(x);

How About This?double x = 3;int a = square(x);

It’s kind of annoying that our square procedure can’t work for real numbers too. We’ll fix this later so that we can have a generic procedure definition that works for both integers and real numbers.

A Quick Fix?static double square(double x){

return x*x;}…double x = 3;double y = square(x); // this works nowdouble a = square(3.5); // and so does thisint b = square(5); // but now this won’t work

A Quick Fix?static double square(double x){

return x*x;}…double x = 3;double y = square(x); // this works nowdouble a = square(3.5); // and so does thisint b = square(5); // but now this won’t work

The complete fix? Much later, or maybe in CS21B.

How About This?boolean a = square(3);

How About This?boolean a = square(3) > square(4);

How About This?boolean a = square(3) + square(4);

How About This?static int square(int x){

return "Hello";}

The void Return Type►Recall: imperative programming

insists that some instructions are statements, and others are expressions.►Expressions always have values.►Statements do not always do.

The void Return Type►Some instructions can be statements

without values.► The print statement doesn’t have an

output value, because it already directly prints out to console. There’s no point keeping its “value” somewhere or using it in another expression.

► Same goes with main.

The void Return Type►So, we invent a new “return type”

called void.►Putting void as a return type for a

procedure definition means that the procedure does something, but does not return a value to the procedure that called it.

Examplestatic void sayFavorite(String name, int number)

{

print(person);

print("'s favorite number is ");

print(number);}

sayFavorite("Grandma", 42);

int x = sayFavorite("Grandma", 42);

Examplestatic void sayFavorite(String name, int number)

{

print(person);

print("'s favorite number is ");

print(number);}

sayFavorite("Grandma", 42);

int x = sayFavorite("Grandma", 42);

Ok. Procedure call is a statement. Prints “Grandma’s favorite number is 42”.

Examplestatic void sayFavorite(String name, int number)

{

print(person);

print("'s favorite number is ");

print(number);}

sayFavorite("Grandma", 42);

int x = sayFavorite("Grandma", 42);Not ok. Procedure call does not produce a value. Alternatively, procedure does not have the correct return type.

Examplestatic void sayFavorite(String name, int number)

{

print(person);

print("'s favorite number is ");

print(number);}

sayFavorite("Grandma", 42);

int x = sayFavorite("Grandma", 42);

By the way, the String is another built-in type over the set of words or series of characters.

Examplestatic void sayFavorite(String name, int number)

{

print(person);

print("'s favorite number is ");

print(number);}

sayFavorite("Grandma", 42);

int x = sayFavorite("Grandma", 42);

Strings can take on series of characters enclosed in double quotes as values.

Question►Why is it possible to

print(square(4));►But not to

print(print(4));?

Question►Why is it possible to

print(square(4));►But not to

print(print(4));? All expressions have

values, but not all procedure calls do.

The print procedure has a void return type specifier.

Outline►The Notion of Types►Abstractions with Data Types►Same Type, Different Implementations

Three Basic Kinds of Answers to Computational Questions

►Number►How far is the earth from the sun?

►Yes/No► Is Venus farther from the earth from the

sun?►Words►What are the planets closer to the sun

than the earth is?

Review: “Problem” Versions for Those Questions

►Number►How far is a given planet from the sun?

►Yes/No► Is this given planet farther from that

given planet from the sun?►Words►What are the planets closer to the sun

than this given planet is?

Three Basic Kinds of Input to Computational Problems

►Number► Given mass and acceleration, what’s the

force?►Yes/No

► Depending on whether the stock goes up or down, what’s the best next investment move?

►Words► Given a search phrase, what are the most

relevant web pages?

Three Kinds of Primitive Data Types

►Numerical Data Types►Central assumption of information

theory: everything can be represented as a number

►Numbers encoded in binary (0s and 1s)

►Usually in many variants for efficiency’s sake

►The Boolean Data Type►True or False►Assign 0 to false, 1 to true

Three Kinds of Primitive Data Types

►Symbolic Data Types► Characters

►Can be represented as numbers, and later encoded to 0s and 1s

► Strings (not really primitive, because Strings are sequences of characters)

► Maybe more advanced A.I. can answer some “how” and “why” questions, but even for those, symbolic data will be enough.

Primitive Data Types in Java►Read about them here:

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Are These Enough?►Consider:►Given the x and y coordinates of a

point, find the x and y coordinates of its reflection across the line with the given A, B, and C coefficients in its equation in general form.

►Overly detailed!►And what if we want to expand to three

dimensions later?

Are These Enough?►Limitation of Java:►Can’t have more than one output value

►Only one return statement per procedure►Only one return value per return

statement►Can’t output both x and y as two

numbers from the same procedure, even if it makes sense to let one procedure simultaneously produce them

Are These Enough?►Other kinds of input:►Sound input►Keyboard/controller input►Biometric sensors

Are These Enough?►Information comes in many physical

kinds.►Light, sound, matter, etc.

►Information comes in many conceptual kinds.►Money, personal records, shapes, etc.

Are These Enough?►Every kind of data can be broken

down into numbers (even symbolic and Boolean data).

►For complex systems though, we don’t want to go to the level of primitives all the time.

Data Abstraction► It’s better to say:

► Given a point, find its reflection across a given line.► Point and line are compound data types.

► Procedure for doing this doesn’t have to worry about how points or lines are represented in terms of primitives, as long as the representation obeys certain properties.► Important point (no pun intended) we’ll get to later.

Adding Fractions: Without Data Abstraction

static int numerator-add(int a, int b, int c, int d) {

return a * d + b * c;

}

static int denominator-add(int a, int b, int c, int d) {

return b * d;

}

Adding Fractions: With Data Abstraction

static Fraction add(Fraction a, Fraction b) {int num = num(a) * denom(b) +

num(b) * denom(a);int denom = denom(a) * denom(b);return makeFraction(num, denom);

}

Interfacing with Primitives► Input to outer procedure (world) might

still be given as primitive data.► To chunk the primitive data, we need to use

constructors.►Output from outer procedure (world)

might still need to be primitive data.► To examine (and print out) the parts of a

chunked piece of data, we need to use selectors.

Interfacing with Primitives►Programs that use Fractions can be

shielded from how the constructors and selectors are implemented.

Adding Fractions: With Data Abstraction

static Fraction add(Fraction a, Fraction b) {int num = num(a) * denom(b) +

num(b) * denom(a);int denom = denom(a) * denom(b);return makeFraction(num, denom);

}

calls to selectors

Adding Fractions: With Data Abstraction

static Fraction add(Fraction a, Fraction b) {int num = num(a) * denom(b) +

num(b) * denom(a);int denom = denom(a) * denom(b);return makeFraction(num, denom);

}

call to constructor

Adding Fractions: With Data Abstraction

static Fraction add(Fraction a, Fraction b) {int num = num(a) * denom(b) +

num(b) * denom(a);int denom = denom(a) * denom(b);return makeFraction(num, denom);

} We now have an add procedure for fractions that doesn’t care about how the fractions are represented as primitives – new barrier of abstraction.We’re operating on a fraction as a whole concept, without worrying about how its parts are built.

Using the Data Abstractionpublic static void main(String args[]){

Fraction x = makeFraction(3, 2);Fraction y = makeFraction(1, 5);Fraction sum = add(x, y);

}

Using the Data Abstraction with Input/Output

public static void main(String args[]){

int a = 3, b = 2, c = 1, d = 5;Fraction x = makeFraction(a, b);Fraction y = makeFraction(c, d);Fraction sum = add(x, y);println(num(sum));println(denom(sum));

}

But How Do We Implement the Following Procedures?

static Fraction makeFraction(int num, int denom) {

// ?

}

static int num(Fraction x) {

// ?

}

static int denom(Fraction x) {

// ?

}

selectors

How does the lower-level data type designer implement these so that the higher-level procedure designer can use a fraction as a single concept?

constructor

Implementing the Data Typestatic class Fraction{

int numerator;int denominator;

}A fraction is made out of two integers.

fields

Implementing the Constructors and Selectors

static Fraction makeFraction(int num, int denom) {

Fraction f = new Fraction();

f.numerator = num;

f.denominator = denom;

return f;

}

static int num(Fraction x) {

return x.numerator;

}

static int denom(Fraction x) {

return x.denominator;

}

Implementing the Constructors and Selectors

static Fraction makeFraction(int num, int denom) {

Fraction f = new Fraction();

f.numerator = num;

f.denominator = denom;

return f;

}

static int num(Fraction x) {

return x.numerator;

}

static int denom(Fraction x) {

return x.denominator;

}

Call to primitive (built-in) constructor:Request for a chunk of memory to store the parts of my data

Implementing the Constructors and Selectors

static Fraction makeFraction(int num, int denom) {

Fraction f = new Fraction();

f.numerator = num;

f.denominator = denom;

return f;

}

static int num(Fraction x) {

return x.numerator;

}

static int denom(Fraction x) {

return x.denominator;

}

Dot operator: call to primitive selectors.

f.numerator is…In English: f’s numerator variable/fieldIn French: numerator du/de là f

The field belongs to the data, not the data type!Each new Fraction has its own numerator and denominator fields.

Practice Programming Problem: Fractions

►Include the implementation for the Fraction data type, constructor, and selectors above in your program.

►Include the add procedure above.►Create procedures for subtracting,

multiplying, dividing, and printing Fractions.

Outline►The Notion of Types►Abstractions with Data Types►Same Type, Different

Implementations

Why Not Directly Use The Primitives?

static Fraction add(Fraction a, Fraction b) {Fraction f = new Fraction();f.numerator =

a.numerator * b.denominator + b.numerator * a.denominator;

f.denominator = a.denominator * b.denominator;

return f;}

Why Not Directly Use The Primitives

►Because the non-primitive constructors and selectors might need to be made non-trivial

►Same reason for not directly using primitive operators only

Implementing Fractions in Lowest Terms

►Two possibilities:►Let the selectors do the reduction►Let the constructor do the reduction

►Another way: use primitive constructors and selectors, and do the reduction in add.

►But now, you have to do it for all the other operations too!

►Messy: mixing data type use and implementation

Selectors Do the Reductionstatic int num(Fraction x) {

int divisor = gcd(x.numerator, x.denominator);

return x.numerator / divisor;}static int denom(Fraction x) {

int divisor = gcd(x.numerator, x.denominator);

return x.denominator / divisor;}

Constructor Does the Reduction

static Fraction makeFraction(int num, int denom) {

Fraction f = new Fraction();

int divisor = gcd(num, denom);

f.numerator = num / divisor;

f.denominator = denom / divisor;

return f;

}

Which Is Better?►Depends on application►Need to access Fractions many times?

Let Fractions be stored in lowest terms (constructor does the reduction).

►Need to create many Fractions? Reduce to lowest terms only when accessing (selectors do the reduction).

Which Is Better?►The procedures that operate on Fractions don’t have to change depending on this decision.

►Yet another way: reduce only when printing►May cause issues when Fractions get

large

Layers of Abstraction for Data

Programs That Use FractionsOperations on Fractions

Constructors for Fractions

Primitive Constructor

Ways to Implement Primitive Constructors

Selectors for Fractions

Primitive Selector

Ways to Implement Primitive Selectors

Practice Programming Problem: Fractions in Lowest

Terms►Implement Euclid’s gcd algorithm (you

may grab a copy from the internet and don’t care that you don’t understand how it works) so that Fractions are printed out in lowest terms.

►Try out the two different ways of implementing the Fraction (in lowest terms) data type. Historical side note:

Euclid’s algorithm is one of the first non-trivial algorithms.

The Fields Can Change Too►Example:►Point given in x-y rectangular

coordinates►Point given in r-theta polar coordinates►Both must have a constructor that

takes and another that takes (there can be multiple constructors for one type).

►Both must have selectors for all four: , , ,

Practice Programming Problem: Points

►Create two different implementations for a 2D Point data type: a rectangular and a polar.

►Make sure each has two constructors►One takes and ►Another takes and

►Make sure each has four selectors► getX, getY, getR, getTheta

Summary►A programming language can have a

type system to enhance meaningfulness and safety.

►Just as with procedures, we use abstraction with data to manage complexity.

►Compound data types can be built out of primitive data types.

Summary►The data type definition is shielded

from its use with constructors and selectors.

►There are different ways to implement a data type.

Next Time…►More combinations with data types►Comparison with procedural

abstraction►Moving from procedural to OOP