Lab 4: Classes and string Objects


Introduction

In today's exercise, we examine a simple language translation problem: converting a word from English into the children's language Pig-latin. Here are some examples of English words and their Pig-latin translation:

EnglishPig-latin EnglishPig-latin
alphabetalphabetyay nerderdnay
billygoatillygoatbay orthodoxorthodoxyay
crazyazycray pricklyicklypray
drippingippingdray quasimodoasimodoquay
eligibleeligibleyay rhythmythmrhay
farmarmfay spryyspray
ghostostghay threeeethray
happyappyhay uglyuglyyay
illegalillegalyay vigilantigilantvay
juryuryjay wretchedetchedwray
killjoyilljoykay xerxeserxesxay
limitimitlay yellowellowyay
messyessymay zippyippyzay

Study the examples given, looking for a "rule" that you can use to translate a word from English into Pig-latin. When you think you have found one, record it and then continue.

The Problem

Today's problem is to complete a program to translate sentences from English into Pig-latin. To do so, we will use a new data type named string. A declaration of the form:

   string englishWord = "farm";
builds the object englishWord as an indexed variable capable of storing multiple characters:

The numbers 0, 1, 2 and 3 are the index values of the characters within englishWord, and can be used to access the individual characters within the string object.

Getting Started

To begin today's exercise, create a pigLatin project/folder in which to save today's work. Then save a copy of translate.cpp in your new pigLatin folder, and open it from within Visual C++, and use the menu choice

   Project -> Add To Project -> Files...
to add translate.cpp to your pigLatin project. This file contains the skeleton of a program for translating a sentence from English into Pig-latin. Take a few moments to familiarize yourself with its contents.

Within translate.cpp, we can see several points of interest:

  1. For storing a sequence of characters, C++ provides the type string. Variable or constant objects of type string can be defined in the same manner as other objects:
          string englishWord, 
                 piglatinWord;
    	
  2. A string value can be read from the keyboard (via cin) into a string variable using the extraction operator (>>), just like any other kind of value:
          cin >> englishWord;
    	
    The >> operator skips leading white space, reads characters into the string object, and stops when it encounters more white space. The effect of operator >> is thus to read the next word of input into the string object.
  3. A string value can be displayed on the screen (via cout) using the insertion operator (<<).
          cout << piglatinWord;
    	
  4. Functions can be written that take string values as arguments, and return string values:
          piglatinWord = Piglatin(englishWord);
    	
  5. Unlike most of the types we have examined thus far, string is the name of a class. A class is a container whose name creates a new type, in which can be stored both data and operations (i.e., functions). The objects in a class that store data are called data members, and the objects in a class that provide operations are called function members.
  6. In addition to string, the types istream and ostream are both classes. The main function of translate.cpp uses the get() function member of class istream, which reads a single character from the stream, without skipping white space, and passes it back to its caller through its argument:
          cin.get(separator);
    	
    Since this call occurs after a string value has been read, separator will at the end of this call contain whatever white space character caused the extraction operator to stop.
  7. One way to understand a call to a function member is the message metaphor. Think of this call as your main function sending cin the message get(), with separator as its argument. When it receives the get() message and an argument, cin reads the next character from the keyboard and stores it in that argument, regardless of whether it is white space or not.
Our task is to write a function Piglatin() that, given an English word, returns its Pig-latin equivalent.

Function Design

As usual, we begin by applying object-centered design to our problem.

Function Behavior, Part I. If we ignore the cases where the vowel is the first character in a word, we might describe the behavior of a function to translate an English word into Pig-latin as follows:

   Receive an English word.  Find the position of the first vowel 
   in that word, checking that a vowel was actually present. (If
   not, our program should display a diagnostic and terminate.) 
   The Pig-latin word is the portion of the word from the first
   vowel to its end, followed by the consonants at the beginning
   of the word, followed by "ay".  Return the Pig-latin word.
Note that in trying to anticipate what might go wrong, a user might enter a word with no vowels, and so we should check for this possibility.

Function Objects. From this description, we can identify the following objects:

Description Type Kind Movement Name
The English word string varying received englishWord
Position of the first vowel int varying local vowelPosition
The Pig-latin word string varying returned piglatinWord
Portion of the English word
from its first vowel until its end
string varying local lastPart
Consonants at the beginning of the English word string varying local firstPart
"ay" string constant local --

This gives us the following specification for our function:

   Receive: englishWord, a string.
   Return: piglatinWord, a string.
Use this specification to create a stub for a function named Piglatin() in translate.cpp; then add a prototype for Piglatin() above the main function.

Function Operations. Our behavioral description lists these operations:

Description Defined? Name Library?
1 Receive englishWord (a string) from caller yes function call
mechanism
built-in
2 Find the position of the first vowel in a string yes find_first_of() string
3 Check that the word actually contains a vowel yes assert() cassert
4 Access the substring of a word from
its first vowel to its end
yes substr() string
5 Access the substring of a word from
its beginning until just before its first vowel
yes substr() string
6 Combine two substrings into a single string yes + string
7 Return a string to the caller yes return built-in

Function Algorithm. Organizing these operations gives us this algorithm:

   0. Receive englishWord from the caller.
   1. Find vowelPosition, the position of the first vowel in englishWord;
      check that a vowel was found.
   2. Build lastPart, the substring of englishWord starting at
      vowelPosition and running to its end.
   3. Build firstPart, the substring of englishWord starting at
      its beginning and ending just before vowelPosition.
   4. Build piglatinWord by concatenating lastPart, firstPart, and "ay".
   5. Return piglatinWord.

Function Coding

Some of these operations in our algorithm are familiar ones (e.g., 0, 5), and require no further work on our part. Others (e.g., 1-4) are less familiar. If we want to avoid "reinventing the wheel", we must investigate the operations (i.e., function members) provided by the string class. To make this easier to do, we have provided a string quick reference containing some of the string function members. (You may want to print a hard copy of it and store it in a convenient place.) As it happens all of the less familiar operations in our algorithm can be performed using string function members.

1. Find vowelPosition, the position of the first vowel in englishWord; check that a vowel was actually found. Since we want to find the first vowel in englishWord, we search the preceding list for a string operation to perform this operation. In the list, we see the find_first_of() function member which can be used for this purpose. The required pattern is a string listing each vowel, and we should begin searching at the first character, whose index is zero. The first line in our function is thus

      int vowelPosition = englishWord.find_first_of("aeiouyAEIOUY", 0);
That is, if englishWord is as follows:

then this statement will search englishWord for the first occurrence of a, e, i, o, u, y, A, E, I, O, U, or Y; beginning with the character at index 0, and will thus return 1, the index of a. Add this statement to the stub of Piglatin().

Our list of string function members also tells us that find_first_of() returns the special constant string::npos if none of the characters in pattern occur in the target. We can use this in an assert() to check that a vowel was found, so add a call to assert() to check that vowelPosition is not equal to string::npos.

Use the compiler to check the syntax of your program thus far (aside from not returning anything), and then continue to the next part of the exercise.

2. Build lastPart, the substring of englishWord starting at its first vowel and running to its end. If we again search through the list of string operations, we see that the substr() operation provides a way to accomplish this step. In our case, we want to grab the substring of englishWord beginning at vowelPosition, and whose length is the number of characters between vowelPosition and the end of englishWord. (Calculating this length is the tricky part.) Since the string function member size() gives us the total number of characters in the string, and the index of the first character is always zero, we can get the length of the substring by subtracting vowelPosition from the size() of the string:

So the second step of our algorithm can be encoded like this:

   string lastPart = englishWord.substr(vowelPosition, 
                                       englishWord.size() - vowelPosition);
As before, use the compiler to check the syntax of this statement, and continue when it is correct.

3. Build firstPart, the substring of initial consonants of englishWord. To perform step 3, we can again use the substr() function member of class string. Since we want to grab the consonants at the beginning of the string, we should start grabbing at index 0. As before, computing the number of characters to grab is the hard part: Since vowelPosition contains the index of the first vowel, and index values start at zero, the value of vowelPosition is also the number of consonants at the beginning of the string:

Using this information, add a statement to Piglatin() that encodes step 3, using the substr() function member. Test its syntax using the compiler, and continue when it is correct.

4. Build piglatinWord by concatenating lastPart, firstPart, and "ay". The operation of combining two or more string values into a single string is called concatenation. For example, the concatenation of the string values "en" and "list" produces the string "enlist", while the concatenation of "list" and "en" produces "listen". Order is thus significant in performing concatenation.

Examining the list of string operations, we see that string concatenation can be performed using the plus (+) operator. This step is thus easily coded as follows:

   string pigLatinWord = lastPart + firstPart + "ay";
Add this statement to Piglatin(), and use the compiler to check the correctness of its syntax; then continue to the last step.

5. Return pigLatinWord. This is a simple return statement, with which you should be familiar. Add the necessary return statement, and then compile and run your program, testing it with words that do not begin with a vowel. If your program compiles correctly but does not correctly translate words beginning with consonants into pig-late, track down your logic error before continuing.

To track down a logic error, run the debugger (F11) and use it to step into your Piglatin() function. Use the step-over command (F10) to step over the calls to find_first_of() and substr(). In the auto window at the bottom of the window, note that the values of lastPart and firstPart are listed as

   {...}
This is because as class objects, lastPart and firstPart are made up of multiple values, including their length, capacity, and the sequence of characters they store. To view these values, click on the boxed plus sign next to their names. The values of the string stored in each object is next to its attribute. If you watch these values as you step through the function, you should be able to see the values change appropriately as each statement executes -- if they do not, you should be able to use the inappropriate value to deduce the error in your logic. When you are satisified with the correctness of what you have written, Stop Debugging and continue to the next step.

What happens if you enter a word that begins with a vowel? Why?

Pig-latin Translation: Part II

In this part of the exercise, you are to extend function Piglatin() with the necessary code to handle words that begin with a vowel. Whereas Part I "led you by the hand", you are to figure out what must be done in Part II (feel free to consult with your lab partner).

Begin by describing how the function's behavior must differ from its current behavior. List any additional objects and/or operations that are required to achieve this new behavior. Modify the algorithm from Part I accordingly and then use it to update the code in Piglatin().

Pictures are often helpful, especially when figuring out proper index values, so try drawing a picture of a sample string if you get stuck.

Phrases you should now understand:

String, Substring, Concatenation, String Input, String Output, String Length, String Replacement, String Searching, String Pattern Matching (Forward and Reverse).


Submit:

A hard copy of your final version of translate.cpp, plus a hard copy showing its execution.


Back to This Lab's Home Page

Back to the Prelab Questions

Forward to the Homework Projects


Copyright 1998 by Joel C. Adams. All rights reserved.