CSCI 159 Lab 4 exercises

Lab 4 is a one week lab: due prior to the start of your lab on Nov. 4th/6th

There are still two halves (part half in basic.cpp and second half in lab4.cpp), but there is no extended independent design section (i.e. no work on the stair counting this time).

Hopefully most people complete nearly the entire lab 4 in their scheduled lab section on Oct. 28th/30th.

Here is the collection of new C++ syntax elements we'll be using for lab4.


Follow our usual setup process

  1. 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)

  2. get lab4:
    make -f make159 csci159/lab4

  3. Go into your lab4 directory and begin the edit/compile/test/submit routine: 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:

We'll need several constants as well:

The main routine will:
  1. call skipThis("part one") and check the return value to see if the user wishes to skip part one. If skipThis returns false then we do want to try partOne here, so the test will look something like
    if (!skipThis("part one")) {
    If the user didn't skip then we want to call partOne and store/print the value it returns (so we can check it did return the value correctly).

  2. do similarly for part two, but here the body of the if can simply call partTwo.

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);

// partOne 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 partOne();

// partTwo 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,
//  partTwo uses partOne to get the number)
void partTwo();

int main()
{
   // run the two parts, giving the user chances to skip either or both
   if (!skipThis("part one by itself")) {
      float positiveNum;
      positiveNum = partOne();
      cout << "Your number was " << positiveNum << endl;
      cout << "End of part one" << endl;
   }
   if (!skipThis("part two")) {
      partTwo();
   }
}

bool skipThis(string section)
{
   return true; // <== we'll replace this when we write the real version of the function
}

float partOne()
{
   return 0; // <== we'll replace this when we write the real version of the function
}

void partTwo()
{
}

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 partOne

(This code will replace the "return 0;" we had in partOne 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 partTwo

We'll need variables to keep track of: To get started, we need to call partOne 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 = square
    // otherwise if square is smaller than originalNum
    //    then set lowerBound = square
    // 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 part 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 part one by itself (Y or N): y
Do you want to skip part 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: pass-by-reference and nested loops (to be done in lab4.cpp)

For the second half of the lab (in lab4.cpp) we'll be practicing with pass-by-reference parameters and nested for loops (loops inside loops).

The actual program behaviour will be getting the user to enter two characters, figuring out which one is "smaller" (using < to test), and using nested for loops to print a triangular display based on that.

Sample run

The output below shows a sample run where the user entered xp as their two characters. (User input illustrated using bold italics here just for clarity)

Please enter two characters, e.g. Qv
xp
Using (p) and (x) as the chars
pqrstuvwx
qrstuvwx
rstuvwx
stuvwx
tuvwx
uvwx
vwx
wx
x

Basic setup:

The only library we'll need this time is iostream, but if you're using the "using std::cout;" approach to simplify cout, cin, endl, etc then you'll need to add one more for noskipws (which we'll discuss later).

We'll be creating three functions:

The behaviour of main is pretty straightforward:

And, of course, we'll need initial empty versions of the three functions down below main.

A sample setup might thus look like:

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
using std::noskipws;

// exchanges the values between the two parameters
void swap(char &x, char &y);

// get two characters from the user,
// put the smaller (according to <) into parameter min
//     and the larger into parameter max
void getChars(char &min, char &max);

// print an alphabet triangle
void printTri(char xAxis, char yAxis);

int main()
{
   // get the two characters and pass them to print the triangle
   char x, y;
   getChars(x, y);
   printTri(x, y);
}

void swap(char &x, char &y)
{
}

void getChars(char &min, char &max)
{
}

void printTri(char xAxis, char yAxis)
{
}

The swap function:

Much like the swap function we considered in class to swap two ints, this:

void swap(char &x, char &y)
{
   char oldX = x;
   x = y;
   y = oldX;
}

The getChars function:

This is a simpler input function than when we're getting numbers from the user, as char input can never cause cin to fail (thus we don't have to worry about cin.fail, clear, ignore).

The function simply has to prompt the user to enter two chars and read them into the two parameters (min and max).

Ordinarily cin for chars skips whitespace (spaces, tabs, etc), but we might want to allow the user to enter these for use in our program, so we'll add >> noskipws to our cin to prevent it from skipping whitespace.

If min > max then we need to reverse their order, so we'll call the swap function.

void getChars(char &min, char &max)
{
   // get the two characters
   cout << "Please enter two characters, e.g. Qv" << endl;
   cin >> noskipws >> min >> max;

   // if the one in max is smaller then swap them
   if (max < min) {
      swap(min, max);
   }
}

The printTri function:

The printTri function uses an outer for loop to control which row of output we're on right now, and an inner loop to control the characters we print for the current row. Once the inner loop completes we can print an endl to finish off the current row.

void printTri(char xAxis, char yAxis)
{
   cout << "Using (" << xAxis << ") and (" << yAxis << ") as the chars" << endl;
   for (char row = xAxis; row <= yAxis; row++) {
       for (char col = row; col <= yAxis; col++) {
           cout << col;
       }
       cout << endl;
   }
}

One interesting note is how the < works when comparing characters:

Try experimenting with different character combinations to get a glimpse of the order of characters in ascii (suggestions: try things like Wf or !5 or t~).

As always, check you've followed code standards and do a make submit