360 likes | 492 Views
Vectors of Vectors. Representing Objects with Two Dimensions. Alan Abrams. Beth Bittner. Charles Chalmers. Debra DeVries. Ed Evans. Fran Ferguson. Gary Grodin. Hannah Hollister. Ingmar Illich. Joe Johnson. Louise Lamar. Mark Morris. Nancy Norris. Obadiah O’Hare. Pam
E N D
Vectors of Vectors Representing Objects with Two Dimensions
Alan Abrams Beth Bittner Charles Chalmers Debra DeVries Ed Evans Fran Ferguson Gary Grodin Hannah Hollister Ingmar Illich Joe Johnson Louise Lamar Mark Morris Nancy Norris Obadiah O’Hare Pam Parker Problem A seating chart is often used by teachers to call on students and simplify attendence tracking: Write a program that reads a seating chart from a file, displays the chart so that the teacher can call the roll, and writes to a file those who are absent.
Behavior Our program should prompt for and read the name of the data file from the user. It should then open an ifstream to that file. It should then read a seating chart from that file. The program should then prompt for and read the name of an output file, and open an ofstream to that file. The program should display the seating chart, and for each person listed on it, our program should ask the user if that person is present. If the reply is ‘n’, the program should write the name of that person to the output file. The program should then close the fstreams and display a ‘processing complete’ message.
Objects Description Type Kind Name in-file name string varying inFileName out-file name string varying outFileName input fstream ifstream varying fin output fstream ofstream varying fout seating chart ??? varying seatingChart person string varying ??? reply char varying reply
Operations display a string yes string << Description Predefined? Library? Name read a string yes string >> open/close fstreams yes fstream -- read seating chart no -- -- print seating chart no -- -- read a char yes -- >>
Algorithm 0. Display purpose of program 1. Prompt for and read name of input file into inFileName. 2. Open fin to inFileName, verify open. 3. Fill seatingChart with data from fin. 4. Close fin. 5. Display seatingChart. 6. Prompt for and read name of output file into outFileName. 7. Open fout to outFileName, verify open. 8. For each student in seatingChart: a. Ask if they are present. b. Read reply. c. If reply is ‘n’: write the student to fout. 9. Close fout. 10. Display a ‘processing completed’ message.
Preliminary Analysis Since we are just storing a student’s name, we can use a string to represent student objects. A seating chart has two dimensions -- rows and columns -- so we cannot represent it with a simple vector without losing information. We therefore need to define a class to represent seating chart objects.
Two-Dimensions One way to represent a 2-dimensional structure is to use a vector of vectors: vector< vector<string> > myChart;
Two-Dimensions One way to represent a 2-dimensional structure is to use a vector of vectors: vector< vector<string> > myChart; The “inner” vector represents a row of the chart.
Two-Dimensions One way to represent a 2-dimensional structure is to use a vector of vectors: vector< vector<string> > myChart; The “inner” vector represents a row of the chart. The “outer” vector represents a vector of rows.
Two-Dimensions One way to represent a 2-dimensional structure is to use a vector of vectors: vector< vector<string> > myChart; The “inner” vector represents a row of the chart. The “outer” vector represents a vector of rows. The spaces around the “inner” vector are required -- without them, the compiler may confuse >> with the extraction (input) operator.
Using typedef Alternatively, we can use the typedef statement to first declare a Row type: typedef vector<string> Row; and then use this new type to declare our chart: vector<Row> myChart; The typedef statement has the following pattern: typedef ExistingType NewType; Its effect is to declare NewType as a synonym or alias for ExistingType. It is commonly used to improve program readability.
Building Classes class SeatingChart { public: private: }; We can thus start a SeatingChart class as follows: int myRows; int myColumns; typedef vector<string> Row; vector<Row> myChart; The data members myRows and myColumns are not required, but they simplify some operations.
Prototypes class SeatingChart { public: private: // ... data members omitted }; SeatingChart(); SeatingChart(int rows, int columns); int Rows() const; int Columns() const; string Student(int row, int column) const; friend istream & operator>>(istream & in, SeatingChart & chart); friend ostream & operator<<(ostream & in, const SeatingChart & chart);
Operation: Default Constructor The default constructor initializes the data members to default values: SeatingChart aChart; Specification: Postcondition: myRows == 0 && myColumns == 0 && myChart.empty() == true.
Default Constructor This is simple enough to define inline in the header file: inline SeatingChart::SeatingChart() { myRows = 0; myColumns = 0; } The default vector constructor automatically initializes myChart as an empty vector< vector<string> >.
aChart myRows 3 myColumns 5 myChart [0] [1] [2] [3] [4] "" "" "" "" "" [0] "" "" "" "" "" [1] "" "" "" "" "" [2] Explicit-Value Constructor This constructor lets you construct a SeatingChart of a specified size (in rows and columns): Specification: Receive: rows, columns, two int values. Precondition: rows > 0 && columns > 0. Postcondition: myRows == rows && myColumns == columns && myChart is a 2-D vector of rows rows and columns columns. SeatingChart aChart(3, 5);
Explicit-Value Constructor This is sufficiently complicated to define separately: // ... #include “SeatingChart.h” SeatingChart:: SeatingChart(int rows, int columns) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; // build: myChart = vector<Row>(rows); // “outer” vector for (int i = 0; i < rows; i++) // and myChart[i] = vector<string>(columns); // “inner” vectors }
Explicit-Value Constructor This is sufficiently complicated to define separately: // ... #include “SeatingChart.h” SeatingChart:: SeatingChart(int rows, int columns) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; // build: myChart = vector<Row>(rows); // “outer” vector for (int i = 0; i < rows; i++) // and myChart[i] = vector<string>(columns); // “inner” vectors } We use one of the three vector constructors to initialize the “outer” vector of myChart.
Explicit-Value Constructor This is sufficiently complicated to define separately: // ... #include “SeatingChart.h” SeatingChart:: SeatingChart(int rows, int columns) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; // build: myChart = vector<Row>(rows); // “outer” vector for (int i = 0; i < rows; i++) // and myChart[i] = vector<string>(columns); // “inner” vectors } We then use the same vector constructor within a loop to initialize the “inner” vectors of myChart.
Explicit-Value Constructor This is sufficiently complicated to define separately: // ... #include “SeatingChart.h” SeatingChart:: SeatingChart(int rows, int columns) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; // build: myChart = vector<Row>(rows); // “outer” vector for (int i = 0; i < rows; i++) // and myChart[i] = vector<string>(columns); // “inner” vectors } This initializes each Row of myChart using the default string constructor (which generates empty strings).
Extractors The extractors retrieve data member values: cout << aChart.Rows() << aChart.Columns(); Specifications: Rows: Return myRows. Columns: Return myColumns. Note that we provide no extractor for myChart, because it contains the actual seating chart.
Default Constructor These are sufficiently simple to define inline: inline int SeatingChart::Rows() const { return myRows; } inline int SeatingChart::Columns() const { return myColumns; }
Element Access The Student() function returns the student assigned to a given seat: cout << aChart.Student(r, c); Specification: Receive: row, column, two int values. Precondition: row is a valid row-index && column is a valid column-index. Return: the string in myChart [row][ column].
Default Constructor This is probably simple enough to define inline: inline string SeatingChart::Student(int row, int column) const { assert(row >= 0 && row < Rows() && column >= 0 && column < Columns()); return myChart[row][column]; } Note that where a single subscript is used to access an element of a normal vector, two subscripts are needed to access an element of a 2-Dimensional array. The first subscript accesses the correct Row of myChart, the second accesses the correct column in that Row.
Input The input operator lets you read a SeatingChart: fin >> aChart; Specification: Receive: in, an istream; chart, a SeatingChart. Precondition: chart is an empty SeatingChart && in leads to a file containing a seating chart. Input: the seating chart, via in. Passback: in, the seating chart read from it; chart, containing the extracted values. Return: in, for chaining.
Input Operator This is sufficiently complicated to define separately. // ... istream & operator>>(istream & in, SeatingChart & chart) { string firstName, lastName; // each name is a word char separator; // for end-of-line for (;;) // loop1: { // (to fill outer vector) Row aRow; // declare clean Row for (;;) // loop2: { // (to fill inner vector) in >> firstName >> lastName; // read person’s name in.get(separator); // read white-space if (in.eof()) break; // stop at end-of-file aRow.push_back(firstname + ‘ ‘ + lastName); if (separator == ‘\n’) break;// stop at end-of-line } // end loop2 if (in.eof()) break; // stop at end-of-file chart.push_back(aRow); // append Row to chart } // end loop1 return in; // allow chaining }
Output The output operator lets you write a SeatingChart: cout << aChart << endl; Specification: Receive: out, an ostream; chart, a SeatingChart. Output: the values in chart, via out. Passback: out, containing the SeatingChart. Return: out, for chaining.
Output Operator This is sufficiently complicated to define separately. Such structures are displayed in rows and columns: // ... ostream & operator<<(ostream & out, const SeatingChart & chart) { for (int r = 0; r < chart.Rows(); r++) { for (int c = 0; c < chart.Columns(); c++) out << chart.Student(r, c) << ‘\t’; out << ‘\n’; } return out; // allow chaining }
Coding Our Algorithm // rollCall.cpp // ... documentation // ... other #includes #include “SeatingChart.h” int main() { cout << “\nTo generate the absentee list,” << “\n enter the input file name“ << “\n containing the seating chart: “; string inFileName; cin >> inFileName; We are now ready to implement our algorithm...
Coding (Ct’d) // ... rollCall.cpp continued ifstream fin(inFileName.data()); assert(fin.is_open()); SeatingChart chart; fin >> chart; fin.close(); cout << “\nEnter the name of the absentee file: “; string outFileName; cin >> outFileName; ofstream fout(outFileName.data()); assert(fout.is_open()); char reply; // ...
Coding (Ct’d) // ... rollCall.cpp continued for (int r = 0; r < chart.Rows(); r++) for (int c = 0; c < chart.Columns(); c++) { cout << “\nIs “ << chart.Student(r,c) << “ present (y or n)? “; cin >> reply; if (reply == ‘n’) fout << chart.Student << ‘\n’; } fout.close(); cout << “\nProcessing complete. Results are in “ << outFileName << endl; }
Summary Objects consisting of values organized in a 2-D structure (i.e., rows and columns) can be represented by a vector of vectors. Accessing an element of a vector of vectors requires two subscript operations: • one to access the correct element of the “outer” vector (i.e., the correct row); and • one to access the correct element of the “inner” vector (i.e, the correct column within that row). If all elements in a 2-D structure must be processed, use two nested for loops.