CSCI 159 Lab 4 exercises
Lab 4 is due prior to the start of your lab 5's
As usual, there are two halves (first half in basic.cpp and second half in lab4.cpp), where the second half
is an extension of our lab3 exercise.
- the basic/warmup exercise (basic.cpp), worth 5 marks
- the design/implementation exercise (lab4.cpp), worth 10 marks
- both parts are due before the start of lab 5
Here is the collection of new C++ syntax elements we'll be using
for lab4.
Follow our usual setup process
- Log in, open a browser, go to the lab4 page, open a terminal window:
(see lab 2 if you need a refresher on any of the steps)
- get lab4:
make -f make159 csci159/lab4
- Go into your lab4 directory and begin the edit/compile/test/submit routine:
for the first half of today's lab:
- to edit basic.cpp: pluma basic.cpp &
- to compile basic.cpp: make basicx
- to test basicx: ./basicx
- to submit: make submit
once we get to the second half of today's lab:
- to edit lab4.cpp: pluma lab4.cpp &
- to compile lab4.cpp to create lab4x: make lab4x
- to test lab4x: ./lab4x
- to submit: make submit
As with previous labs, you'll see that the two .cpp files are nearly empty to start with.
First half: the basics of loops and booleans (to be done in basic.cpp)
We'll be using the string and iostream libraries for basic.cpp, and writing three functions:
- skipThis (takes a string parameter and returns a char): the string parameter describes
a section (step one or step two), and the function
asks the user Y/N do they wish to skip this section. It gets their response (using a do while
loop to repeat until they answer one of YyNn) and returns true if they wish to skip, false otherwise.
- stepOne (takes no parameters and returns a float): the function asks the user to
enter a number > 1, gets and checks their response (Using a while loop to repeat
until a valid value is given), and returns the eventual valid answer.
- stepTwo (takes no parameters and has a void return type): this uses stepOne to
get a number from the user then uses a for loop to calculate a series of better and better
estimates for the square root of that number.
We'll need several constants as well:
- a const char, 'Y', specifying which character represents yes
- a const char, 'N', specifying which character represents no
- an integer, 10, specifying how many estimates our root calculation will work through
- an integer, 80, specifying how many characters to discard on bad input
The main routine will:
- call skipThis("step one") and check the return value to see if the user wishes to
skip step one. If skipThis returns false then we do want to try stepOne
here, so the test will look something like
if (!skipThis("step one")) {
If the user didn't skip then we want to call stepOne and store/print the value
it returns (so we can check it did return the value correctly).
- do similarly for step two, but here the body of the if can simply call stepTwo.
The starting code will thus look something like this (with empty implementations of
the three functions down at the bottom):
// set which characters we'll for yes/no
const char YES = 'Y';
const char NO = 'N';
// set the maximum number of input characters to clear on broken input
const int LineLen = 80;
// how many estimates we'll go through in our square root calculations
const int AccuracyIterations = 10;
// asks the user if they want to skip a section (Y or N)
// repeats the question (using do while) until they enter Y or y or N or n
// returns true if they want to skip, false otherwise
bool skipThis(string section);
// stepOne uses a while loop to get a number greater than one from the user then returns it
// (repeats until a valid value is supplied)
float stepOne();
// stepTwo uses a for loop to approximate the square root of a number
// (each pass through the loop gets a better approximation than the previous pass,
// stepTwo uses stepOne to get the number)
void stepTwo();
int main()
{
// run the two steps, giving the user chances to skip either or both
if (!skipThis("step one by itself")) {
float positiveNum;
positiveNum = stepOne();
cout << "Your number was " << positiveNum << endl;
cout << "End of step one" << endl;
}
if (!skipThis("step two")) {
stepTwo();
}
}
bool skipThis(string section)
{
return true; // <== we'll replace this when we write the real version of the function
}
float stepOne()
{
return 0; // <== we'll replace this when we write the real version of the function
}
void stepTwo()
{
}
|
Writing skipThis
(This code will replace the "return true;" we had in skipThis originally.)
This is meant to prompt the user to enter Y or N (for yes or no) and return
true if they said Y or false if they said N.
We'll use a do-while loop to repeat until they enter something valid,
and we'll convert the character they enter to uppercase (so they can enter y or n
in addition to Y or N).
The nice thing about char input is that it cannot cause cin to fail,
so we don't need to worry about cin.fail, cin.clear, and cin.ignore.
In this case, we'll use a boolean variable (validAnswer) to keep track of whether
or not we've seen valid input from the user. This will be set to false initially
(since we haven't seen any input from the user yet) and will be changed
to true once we have valid input.
Thus our loop structure is something like
char skipOrNot; // the variable where we'll store the user input
bool validAnswer = false; // do we have valid input yet?
do {
// get the user to enter their character, store it in skipOrNot
// convert it to uppercase
// if it's yes then we can simply return true (we're done, they want to skip)
// otherwise if it's no then we can set validAnswer to true
// (we have a valid answer, asking not to skip)
// otherwise we can display an error/try again message
} while (!validAnswer);
return false; // we got out of the loop because they said No to skipping
Remember you have the constants YES and NO, be sure to use those rather
than hard-coding 'Y' and 'N' inside your function.
Converting to uppercase is done by the 'toupper' function (from the cctype library, which
is included for you by the string library), e.g.
myvar = toupper(myvar); // where myvar is a char variable
Writing stepOne
(This code will replace the "return 0;" we had in stepOne originally.)
This will look more like our earlier exercises where a function gets and checks a number,
but we're doing it in a while loop instead of using recursion.
We'll need a float to store the user number in, and (like in skipThis) we'll use a boolean
variable to keep track of whether or not we've obtained a valid value from the user.
Our loop logic can thus look something like:
float finalNum;
bool validNumber = false;
while (!validNumber) {
// do your cout and cin to get the user's input
// if cin.failed then give them an error/try again message
// (but no recursive call this time, since the loop will take care of repeating)
// otherwise if the number is <= 1 give them an error/try again message
// (again, no recursive call in this lab)
// otherwise set validNumber to true, so the loop will stop
// when it gets checked at the beginning of the next pass
}
return finalNum;
Writing stepTwo
We'll need variables to keep track of:
- the number we're computing the root of, e.g. originalNum (a float)
- our latest estimate, e.g. estimate (a float)
- the square of the latest estimate, e.g. square (a float)
- upper and lower bounds on the root values, e.g. lowerBound, UpperBound (also floats)
- which estimate # this is (estimate 1, estimate 2, etc), e.g. estimateNum (an int)
To get started, we need to call stepOne and assign its return value to originalNum,
and as our initial lower/upper bounds on the square root of that we can use 1 and originalNum
(e.g. if originalNum is 12 we can safely assume its square root is somewhere in the range 1..12).
Our for loop will then go through each of the estimates, getting more accurate each time:
for (estimateNum = 1; estimateNum <= AccuracyIterations; estimateNum++) {
// set our new estimate to be (lowerBound + upperBound)/2
// set square to be (estimate * estimate)
// if square is bigger than originalNum
// then set upperBound = estimate
// otherwise if square is smaller than originalNum
// then set lowerBound = estimate
// otherwise
// tell the user we got the exact answer (!)
// and 'break' from the loop
}
output our final estimate, square, and the originalNum we were trying to compute the root of
Sample run
The output below shows a sample run where the user chose to skip step one and entered
12 as the number to compute the root of.
(User input illustrated using bold italics here just for clarity)
Do you want to skip step one by itself (Y or N): y
Do you want to skip step two (Y or N): n
Please enter a number greater than 1.0: 12
Guess number 1: 6.5
Guess number 2: 3.75
Guess number 3: 2.375
Guess number 4: 3.0625
Guess number 5: 3.40625
Guess number 6: 3.57812
Guess number 7: 3.49219
Guess number 8: 3.44922
Guess number 9: 3.4707
Guess number 10: 3.45996
Our final estimate was 3.45996, whose square is 11.9713 (aiming for 12)
|
As always, check you've followed code standards and do a make submit
Second half: design and implementation problem with loops (to be done in lab4.cpp)
This time we'll extend our lab3 still further, but using the three different kinds of loops
for our repetition:
- Write a function that asks the user how many containers they wish to process,
gets and checks their response, and repeats until a valid response is provided, where
a response is valid if it is an integer value greater than 0.
This function must use a while loop for its repetition.
- Modify the main routine to call the function above and then, using a for loop
for the repetition, process the desired number of containers.
- Modify your recursive function from lab3 to get the size of a container,
this time using a do-while loop for the repetition.
A sample run of the program might thus look something like:
Welcome to HowManyFit!
We estimate how many (small) marbles, ping pong balls, and tennis balls
fit in a container whose size you specify.
Please enter the number of containers you wish to process:
foo
Error: that was not a number, please try again
Please enter the number of containers you wish to process:
2
Please enter a name for the container (as a single word): carton
Please enter the size of a(n) carton in litres (e.g. 4.6): no
Sorry, that was not a valid number, please try again
Please enter the size of a(n) carton in litres (e.g. 4.6): -1
Sorry, the size must be a positive number, please try again
Please enter the size of a(n) carton in litres (e.g. 4.6): argh
Sorry, that was not a valid number, please try again
Please enter the size of a(n) carton in litres (e.g. 4.6): ick
Sorry, that was not a valid number, please try again
Please enter the size of a(n) carton in litres (e.g. 4.6): 2.7
A carton of size 2.7 litres holds
189.5 to 231.7 small marbles
49.8 to 60.9 ping pong balls
9.0 to 11.0 tennis balls
Please enter a name for the container (as a single word): bigcarton
Please enter the size of a(n) bigcarton in litres (e.g. 4.6): 7.1
A bigcarton of size 7.1 litres holds
498.4 to 609.2 small marbles
131.0 to 160.1 ping pong balls
23.6 to 28.9 tennis balls
Thanks for using HowManyFit!
As always, check you've followed code standards and do a make submit