You are on page 1of 42

Wednesday, October 27

th

Function Review
Program Design and Decomposition
Typecasting
If we have some time, well start ASCII

Function Review
The foo function
computes the average
of two numbers and:

VOID
int
int foo(int
foo(int
foo(int n1,
n1,
n1, n2,
int
int int
n2,
n2, avg)
int
int avg)
&avg)
({
int sum;= 0;
sum += num1;
sum += num2;

1. Returns the sum of the


numbers.
2. Sends back the average
of the numbers in the
third reference
parameter.

avg
= sum / 2;
return(sum);
avg
= sum / 2;
return(sum);
}]
int main(void)
{
int mean, sum;

Whats wrong with it?

sum = func(5,9,mean);
cout << sum << << mean;
}

Function Review
float credit;
void charge(float amount)
{
if (amount <= credit)
{
cout << Approved.\n;
credit -= amount;
}
else
cout << Rejected!\n;
}
int main(void)
{
credit = 100.00;
charge(52);
charge(10);
charge(40);
}

// starting
// line 1
// line 2
// line 3

What does the


following program
print out?
What is the value
of credit after
lines 1, 2 and 3
have executed.

Function + Static Review


6
1
3

float moving_avg(float cur) // avg of last 3


{
static float s1,s2,s3;
s1=s2;
s2=s3;
s3=cur;

cur
s1
s2
s3

(0+0+3)/3
(0+3+6)/3
(3+6+1)/3

return((s1+s2+s3)/3);

61
3
0
3
0
3
6
3
0
16

}
int main(void)
{
float avg;
avg=moving_avg(3);
avg=moving_avg(6);
avg=moving_avg(1);
avg=moving_avg(2);
}

3
avg 3.3
1
cout
cout
cout
cout

<<
<<
<<
<<

avg
avg
avg
avg

<<
<<
<<
<<

\n;
\n;
\n;
\n;

What does it
print?

1
3
3.3
Etc

Reference Review
void Silly(int byValue, int &byReference)
{
byValue = 10;
byReference = 20;
}
void main()
{
int a = 5, b = 6;
Silly(a,b);
cout << a << << b;

When we pass by
value, the program
makes a copy of
the actual
parameter and
sends it to the
function.

When we pass a variable without using a


reference, it is called passing by value.
When we pass a variable using a reference, it
is called passing by reference.

Reference Review
void Silly(int byValue, int &byReference)
{
byValue = 10;
byReference = 20;
}
void main()
{
int a = 5, b = 6;
Silly(a,b);
cout << a << << b;

When we pass by
reference, the
program
references the
actual parameter
in the calling
function.

When we pass a variable without using a


reference, it is called passing by value.
When we pass a variable using a reference, it
is called passing by reference.

Reference Review
void Silly(int byValue, int &byReference)
{
byValue 10
5
byValue = 10;
byReference = 20;
byReference
}
void main()
{
int a = 5, b = 6;
Silly(a,b);
cout << a << << b;
}

Output:
5 20

a 5
b 20
6

What does it
5print?
5

Programming Challenge
void main(void)
{
int eyes, ears;
cin >> eyes >> ears;
switch (eyes + ears)
{
case 0:
cout << I think ;
case 1:
cout << you are a
goblin;
break;
case 2:
case 3:
cout << You ghoul ;
case 4:
cout << or a human;
break;
default:
cout << not a ghost;
break;
}
}

Convert the switch


statement to the
left into the
equivalent set of
if statements.

Top Down Design


Today were going to learn how to attack
large programming problems

By breaking a big problem down into smaller, more


manageable components, we make it easier to solve.

Top Down Design


Imagine if you were redecorating a bedroom
Youd probably want to break up the task into a set of smaller
steps:
Remove all furniture.
Remove carpet.
Strip old wallpaper.
Fix the drywall.
Paint the room.
Wallpaper the room.
Lay new carpet.

Remove doorway grips.


Unhook carpet from edges.
Roll up carpet.
Carry out of room.

And each of these steps can be further refined

Top Down Design


By decomposing this decorating problem, we can turn a
big job into a set small, manageable tasks.

In a similar fashion, we can decompose complex


programs into simpler components.

Learning how to decompose complex problems is a


critical programming skill, and its difficult to master
without lots of practice.

Top Down Design


The game of 21 Pickup is a two-player game
We start with a pile of 21 stones
Players take turns removing 1,2,or 3 stones from the pile
The player that removes the last stone wins

Goal: Write a program that plays 21 Pickup

Top Down Design


If this is your first time programming, its difficult to
figure out where to start

How do you break the problem down?

Top Down Design Process


Step 1: Understand the problems requirements
A. What must your program do to be correct?
B. What are the inputs to your program?
C. What are the outputs from your program?
D. Are there any special cases to consider?
For class projects, well provide you with the
requirements But in the real world youll often have
to identify them yourself.

21 Pickup Requirements?
Will the computer:
be one of the players or
simply run a game between two human players?
And which player goes first?

How will the program display the progress of the game?


Will we use graphics or just simple text?

How will the program prompt human player(s) for their


moves?
Come up with a detailed spec!
What if the user enters an invalid move?

What happens when a game is over?


What algorithm will the computer player use to choose its
moves?

Our Requirements
The computer will play against a human player
The human player will always go first

When the program starts, it will print out instructions


To play 21 Pickup, each player blah blah blah

The computer will choose a random number of stones each round


This is what the programs output should look like each round:
There are X stones left.
Please enter # of stones to remove:
Then the user types in the number of stones
If the user types an invalid number of stones, the program will:
print Invalid # of stones. Try again.
Prompt the user another time

The computer takes Y stones.

When one player wins, the program will display:


The computer wins or The human wins

After the game is over, the program will end

Top Down Design Process


Step 2: Decompose the problem into independently
solvable, smaller components
A. Break the problem up into simple components
B. Create a specification for each component:
Identify inputs, outputs and pseudo-code
C. Determine the relationship between components

21
Pickup

Run
The
Game

Display
Instructions

Get
Users
Move

Get
Computers
Move

Get The Computers Move


Inputs:
The # of remaining stones
Outputs:
How many stones the
computer player picks up
Pseudo-code steps:
1. If 3 or fewer stones remain,
then we want to pick up all
remaining stones.
2. Otherwise, pick a random #
between 1 and 3
3. Return the chosen value

How to Decompose?
Decomposition can work well
example: designing a restaurant menu
idea: decompose by menu courses
Choose style
and theme

Design appetizers menu


Design entrees menu
Design desserts menu
Design drinks menu

Assemble
pieces

But not always!


example: writing a play
idea: decompose by characters
Choose set of
character parts

Write character 1s part


Write character 2s part
Write character 3s part
etc

Goal: Find the right decomposition!

Merge the
story

* Source, U of Toronto CS Dept

Pseudocode
Pseudocode is an outline of a program that can easily
be understood and converted into valid programming
statements.
Pseudocode enables you to concentrate on the
algorithms without worrying about all the syntactic
details of the programming language.
Pseudocode can vary from general to very detailed.
It should always be descriptive enough to explain
your programs approach to another programmer.

Example Pseudocode
Pseudocode: Converting Fahrenheit to Celsius
Input: A Fahrenheit value
Output: A Celsius value
Subtract 32 from the Fahrenheit value
Multiply the result of the previous step by 5
Divide the result of the previous step by 9
Print out the result

21 Pickup Decomposition
#4 Run
The
Game

#1 Display
Instructions

#3 Get
Users
Move

#2 Get
Computers
Move

#1 Display Instructions
The purpose of this component is to
display instructions to the user.
Inputs:
None
Output:
None
Pseudo-code:
1. Print out the instructions for the user

21 Pickup Decomposition
#4 Run
The
Game

#1 Display
Instructions

#3 Get
Users
Move

#2 Get
Computers
Move

#2 Get Computers Move


The purpose of this component is to
decide the computers next move.
Inputs:
The number of remaining rocks
Output:
The number of rocks the computer wants
to pick up (1-3)
Pseudo-code:
1. If 3 or fewer stones remain, then we
want to pick up all remaining stones.
2. Otherwise, pick a random number
between 1 and 3
3. Return the chosen value

21 Pickup Decomposition
#4 Run
The
Game

#1 Display
Instructions

#3 Get
Users
Move

#2 Get
Computers
Move

#3 Get Users Move


The purpose of this component is to
ask the user for his/her next move.
Inputs:
The number of remaining rocks
Output:
The number of rocks the human player
wants to pick up (1-3)
Pseudo-code:
1. Print out: Please enter # of stones to remove:
2. Get the number from the user
3. While the users # is less than 1, greater
than 3 or greater than the number of
remaining stones, repeat steps #1 and #2
4. Return the chosen value

21 Pickup Decomposition
#4 Run
The
Game

#1 Display
Instructions

#3 Get
Users
Move

#2 Get
Computers
Move

#4 Run The Game


The purpose of this component is to
coordinate the entire game.
Inputs:
None

Output:
None

Pseudo-code:
1. Print the game instructions using component #1
2. Create an initial pile of 21 stones
3. Remember that the human starts first
3. While there are stones left
A. Print out the current board status
B. If its the humans turn, ask component #3 for a move
C. Otherwise, ask component #2 for the computers move
D. Remove the specified number of stones from the pile
E. If the game was won, print the outcome and stop
F. Otherwise, its the other players turn

Top Down Design Process


Step 3: Implement each component using one or more
C++ functions
A. If two components are very similar, consider writing
a single function for both components.
B. Another programmer should be able to use your
function just by reading its input and output specs.
Get Computers Move
Inputs:
The number of remaining rocks
Output:
The number of rocks the computer wants
to pick up (1-3)
Pseudo-code:
1. If 3 or fewer stones remain, then we
want to pick up all remaining stones.
2. Otherwise, pick a random number
between 1 and 3
3. Return the chosen value

int GetComputerMove(int rocksleft)


{
int num_to_pickup;
if (rocksleft <= 3)
num_to_pickup = rocksleft;
else
num_to_pickup = rand() % 4 + 1;
return(num_to_pickup);
}

Top Down Design Process


Step 4: Unit test each component in isolation
A. Use your pseudo-code and input/output specs to
identify test cases
Consider boundary conditions and bad parameters!
B. Write a test main( ) to call and test your component
C. Fix any bugs you find
D. Save your test cases in case you need to retest later
main() // test GetComputerMove
{
Testing
criteria for our
int rocks;
rocks = GetComputerMove(3);
GetComputerMove
if (result != 3)
function:
cout << Error!;
...
}

If we
pass in
1
2
3
>3
<= 0

We Expect
this Result
1
2
3
1 <= rand <= 3
<= 0

Top Down Design Process


Step 5: Combine tested components into a single program
...
A. If
you dont have time to finish a component, just
void PrintInstructions(void)
{
write
an empty placeholder function for it
// placeholder: Ill add code here later!

}
(This
enables you to test the parts of the
void RunTheGame(void)
program
that you have finished.)
{
PrintInstructions();
int numStones = 21, curplayer = 0;
while (numStones != 0)
{
cout << There are << numStones << stones left.\n;
if (curplayer == 0)
numStones -= GetHumanMove(numStones);
else
...
}
main()
{
RunTheGame();
}

Top Down Design Process


Step 6: System test your entire program (or as much
is completed) to ensure it works as a whole
A. Identify appropriate test cases
Consider boundary conditions and bad parameters
B. Test your whole program, fixing any bugs you find
E. Save your test cases in case you need to retest later

Top Down Design Process


Step 7: Identify any components that might be useful
in other programs.
A. Often youll write a function, like one that sorts a
group of numbers, that is widely useful.
B. Keep these functions handy to use in other programs
C. Since theyre already tested, theyre like free code!

Refactoring
So youve completed your version of 21 Pickup and you
have a few spare hours to waste
Old
computer
player:
New computer
player:
You
decide to
improve (refactor)
your computer
Inputs:
player so it playsInputs:
a better game.
The number of remaining rocks
Output:

The number of remaining rocks


Output:

The number of rocks the computer


wants to pick up (1-3)

The number of rocks the computer


wants to pick up (1-3)

Pseudo-code:
1. If 3 or fewer stones remain, then
pick up all remaining stones.
2. Otherwise, pick a random number
between 1 and 3
3. Return the chosen value

Pseudo-code:
1. If 3 or fewer stones remain, then
pick up all remaining stones.
2. Otherwise try to take enough
stones to leave the remaining pile
with a multiple of four stones
3. Return the chosen value

Refactoring
Old computer player:

Inputs:

Inputs:

The number of remaining rocks

The number of remaining rocks


Output:
The number of rocks the computer
wants to pick up (1-3)
Pseudo-code:
1. If 3 or fewer stones remain, then
pick up all remaining stones.
2. Otherwise, pick a random number
between 1 and 3
3. Return the chosen value

New computer player:

same

Output:
The number of rocks the computer
wants to pick up (1-3)
Pseudo-code:
1. If 3 or fewer stones remain, then
pick up all remaining stones.
2. Otherwise try to take enough
stones to leave the remaining pile
with a multiple of four stones
3. Return the chosen value

Notice that our new component still accepts the same inputs
and returns the same outputs.
We can use our updated component without having to
make any changes to the rest of our program!
This is a good thing! Our smart design has isolated
the impact of small changes to our program.

Program Decomposition Conclusion


By breaking a big problem down into smaller, more
manageable components, we make it easier to solve.
Identify each components inputs, output and pseudocode.
Design components such that small changes to one
component dont require changes to other components.
Build and test each component separately and then
test them all together when youre done.
Save useful components for later so you can save time.

Type Casting
Question: What happens
if you try to put an 8x10
photo in a 4x6 frame?
Answer: You end up losing
some of the information in
the picture.

Type Casting
Storing a floating point value into an integer
int main(void)
// example #1
ding 1.235
{
dong 1
double ding = 1.235;
Integer variables can only hold whole
int dong;
numbers.
dong = ding;
}
int main(void)
{
double ding;
int dong = 55;
ding = dong;
}

// Any
Theyre
lessproblem
precise here?
than doubles or1.235
floats
(which can hold fractional numbers too).
// example #2

So when you copy a double or float value


into an integer variable, the integer loses
ding 55.0
the fractional part...

dong 55
This can sometimes result in bugs!
// Any problem here?

Type Casting
When we assign a variable of one type (e.g. int)
to a variable or expression of another type (e.g.
float), this is called type casting.
We have to be careful when doing this because
we may lose precision during the conversion.
There are two ways to deal with type casting in C++:
1. Let the compiler do it for you automatically.
2. You do it yourself with a special C++ command.

Automatic Type Casting


Its OK to set a variable of higher precision to an
variable/value/expression of lower precision.
(Its like putting a small picture in a bigger frame.)
int main(void)
{
int
unsigned int
short
float
double
d
d
i
s
i
i
u
i

i=1;
u=4000000000;
s=2;
f=3.5;
d=4.2;

These conversions are


done automatically by
the compiler!

= f; ? // OK double holds more precise #s than a float


= s; ? // OK double holds more precise #s than a short
= s; ? // OK ints are a superset of shorts
= i; ? // BAD! shorts cant hold every possible int value
= 3.0;? // BAD! ints cant hold fractional numbers
? // BAD! ints cant hold fractional numbers
=3.0*s;
= i; ? // BAD! unsigned ints cant hold neg numbers!
= u; ? // BAD! ints cant hold #s larger than 2 billion

Explicit Type Casting


If you want to set a variable of lower precision to
an expression of higher precision, you should use
an explicit type cast.
This tells the compiler: I meant to do that!
int main(void)
{
float
float
int

// find # of peaches we can put in a bag


bagVolume = 20.5;
peachVolume = 7;

// cubic inches
// cubic inches

howMany;

// bad!
howMany = bagVolume/peachVolume;
howMany = static_cast<int>(bagVolume/peachVolume);// good!
}

cout << howMany << peaches will fit.\n;

Syntax for Explicit Type Casting


This means:
Convert the expression on the right to an
integer before storing it in the variable on the
left.
int main(void)
{
float
float
int

// find # of peaches we can put in a bag


bagVolume = 20.5;
peachVolume = 7;

// cubic inches
// cubic inches

howMany;

howMany = static_cast<int>(bagVolume/peachVolume);// good!


}

Automatic Type Casting


Challenge: Show how you would do explicit type casting
for the each of the assignments below (if necessary).
int main(void)
{
int
unsigned int
short
float
double
d
d
i
s
i
i
u
i

= f; ?
= s; ?
= s; ?
= i; ?
= 3.0;?
?
=3.0*s;
= i; ?
= u; ?

i=1;
u=4000000000;
s=2;
f=3.5;
d=4.2;

// no explicit typecasting necessary!


// no explicit typecasting necessary!
// no explicit typecasting necessary!
// s = static_cast<short>(i);
// i = static_cast<int>(3.0);
// i = static_cast<int>(3.0*s);
// u = static_cast<unsigned int>(i);
// i = static_cast<int>(u);

Type Casting in Arithmetic Expressions


int main(void)
{
double

shhhh;

shhhh = 5/2;
cout << shhhh << endl;

// what will print?

shhhh = 5/2.0;
cout << shhhh << endl;

// what will print?

shh = 1.0*(5/2);
cout << shhhh << endl;

// what will print?

1. A mathematical expression with all integer values is


evaluated using whole number arithmetic: 5/2 -> 2

2. A mathematical expression with one or more floating-point


values is evaluated using floating-point arithmetic: 5/2.0=2.5
3. A sub-expression in parentheses with all integer values
is evaluated first using whole number arithmetic. The wholenumber result is then used in the rest of the expression.

Type Casting in Arithmetic Expressions


int main(void)
{
double
int

shhhh;
a=5, b=2;

shhhh = a/b;
cout << shhhh << endl;

// what will print?

shhhh = static_cast<float>(a/b);
cout << shhhh << endl;

// what will print?

shhhh = static_cast<float>(a)/b;
cout << shhhh << endl; // what will print?
}

1. First the integer division happens (5/2 -> 2), and then
the result is implicitly converted into a double. Result: 2.0
2. Again, first the integer division happens (5/2 -> 2), and then
the result is explicitly coverted into a double. Result: 2.0
3. In this case, we convert as integer value 5 into 5.0, and then
divide the floating point 5.0 by 2. Result: 2.5

Traditional C Typecasts
You may also see this alternative
typecasting notation in C or C++ programs:
variable1 = (newtype) variable2;
variable1 = (newtype) (expression2);
int main(void)
{
float
double
int
short
f = (float)us;
i = (int)(f/2);
s = (int)f /(int)d;
}

f
d
i
s

=
=
=
=

2.718;
3.14159;
-2;
32767;
// f = static_cast<float>(us);
// i = static_cast<int>(f/2);
// s = static_cast<int>(f) /
static_cast<int>(d);

You might also like