Lesson 16Ricardo Salazar, Pic 10A
Storing data in lists● Suppose we want to keep track of a list of 4
numbers:○ E.g: 10, 2015, 0, -1
■ We could create 4 int variables.int value1, value2, value3, value4;
● But this does not scale well. What if we have a list of 10 numbers? What if we have 2018?
● Now imagine a huge database… ● … or an 8 megapixel image!● In C++ a list of numbers can be stored in a single
variable.● It is a nice feature. Without it programming would
be very hard.
Arrays and Vectors● There are at least two options for storing data in lists:
○ arrays, and ○ vectors.
● Arrays are old-fashioned and simple.○ They are built-in primitive data types in C++ … ○ … which means: No helper functions.
● Vectors are more recent. Think of them as fancier arrays with some special features.○ They are defined in the <vector> library, and○ they come with member functions.
● The usage for both types is very similar... ○ but there are some subtle differences.
(6.1) Vectors● vector allow us to store a list of variables/objects
that belong to the same class.○ To use vectors we need to statement: #include <vector>.
● Syntax:vector<dataType> variableName(size);
○ where size is a nonnegative integer.○ If omitted, it defaults to 0.
● To create a vector of 10 integers writevector<int> myVector(10);
● Any data type can be used. Even classes…vector<string> listOfWords(2018);vector<BikeFrame> sieteOnceTeamBikes(9);
(6.2) Accessing vectors● To access the element of a vector at position i, use
the brackets notation [i]. For example, the code:vector<int> myVector(10);myVector[7] = 3.14;
○ Stores 3.14 in the eighth position of myVector.● Vector indices run from 0 to size-1.
○ Just like strings… ○ … because a string is a vector of char elements.
● Similarly: ○ string comes with length(), and ○ vector comes equipped with size().E.g: for (int i=0 ; i < myVector.size() ; i++ )
cout << myVector[i] << endl; // Displays all elements of myVector
ExamplesStore the first 50 odd integers:
vector<int> odds(50);for (int i=0; i<50 ; i++) odds[i] = 2*i+1;
Read 99 doubles and compute the average:
vector<double> v(99);double sum = 0.0;for (int i=0; i<v.size(); i++){ cin >> v[i]; sum += v[i];}cout << "Average: " << sum/v.size() << endl;
Read 10 words, store them, and find the longest one.
vector<string> w(10);string longest = "";
for (int i=0; i<w.size(); i++){ cout << "Word " << i+1 << ": "; cin >> w[i];
int n = w[i].length(); int m = longest.length(); if ( n > m ) longest = w[i]; }cout << "Longest word : " << longest << endl;
● Vectors can grow or shrink.● To add one more value to the back end of a vector
use the member function call push_back(value).vector<double> myVector(4);// read the numbers 3, 3.1, 3.14, 0.1myVector.push_back(4.13);
push_back and pop_back
3 3.1 3.14 0.1
3 3.1 3.14 0.1 4.133 3.1 3.14 0.1
● To remove the last element of the vector, call the member function pop_back().
myVector.pop_back();
3 3.1 3.14 0.1 4.13
size increases to 5
size decreases to 4
Vectors are dynamic● Suppose we are to read a list of doubles from the
user but we don't know how many will be entered.● We can start with an empty vector and push_back
numbers until cin fails.vector<double> manyNumbers; // size defaults to 0double newEntry;while ( cin >> newEntry ) manyNumbers.push_back(newEntry);
● manyNumbers now stores all entered numbers.● If we want to know how many there are, we can call
the size member function… cout << " You entered : " << manyNumbers.size() << " numbers." << endl;
Resizing vectors● The resize member function resets the vector to a
given newSize.vector<int> v(10); // v starts with 10 elementsv.resize(20); // Now v has 20 elements
● If ○ newSize > oldSize, default elements are added.○ newSize < oldSize, elements off the back are deleted.
v.resize(5); // Only the first 5 elements are kept
● If we want to start over and go back to an empty vector we can resize it back to 0.
v.resize(0); //Now v is an empty vector.
Example (card hands) ● In card games, each player has a set of cards (hand).● Let us build a Card class.
○ Let us have the constructor draw a random card.○ Let us also code two 'getters' to retrieve the rank and suit.○ I'll give you a few minutes…
● Now let us build a Hand class using our Card class.class Hand { public: Hand(); . . . private: // The list of cards. vector<Card> cards;};
○ What functions should this class have?
The Hand class● Every time the player draws a card, we should add it
to the vector cards in our hand:void Hand::drawCard() { Card newCard; //Creates a random card. cards.push_back(newCard); return;}
● When a round of play is over, the player turns in all the cards:
void Hand::resetHand() { cards.resize(0); return;}
Casino revisited (blackjack a.k.a. 21)
● In Blackjack a player plays against the dealer.● Whoever gets the sum of their card values closest to
21 without going over, wins.● The dealer draws cards until the sum is bigger or
equal to 17.● Assuming we have a sum() member function in our
Hand class that adds up the card values…Hand dealer;while ( dealer.sum() < 17 ) dealer.drawCard();
● Once the round is over the dealer turns in the cards.dealer.resetHand();
(6.3) Vectors in functions● To pass a vector to a function use
returnType functionName(vector<type> vectorName){
● We do have to tell the function what type of data the vector holds.
● But it is not necessary to tell the function the size of the vector. ○ It is available through the size() function.
void print( vector<int> v ) { for ( int i=0 ; i<v.size() ; i++ ) cout << v[i] << " "; return;}
Vectors in functions (cont)● A function CAN return a vector.
○ Ex: Extract the int sub-vector between positions s and f of a given vector.
vector<int> extract(vector<int> v, int s, int f){ vector<int> sub( f - s + 1 ); // start empty for ( int i=s ; i<=f; i++ ) sub[i-s] = v[i]; // push_back instead return sub;}
○ What would be the result if instead of sub[i-s] = v[i] we tried v[i-s] = v[i] and changed the return type to void?
-1 3 7 2 -4 5 8 7 2 -4
s f
Example● Functions CAN return vectors. Did I say so already?
○ Ex: Read a list of numbers from the user and put them in a vector. Stop when they do not enter a number.
○ Recall that (cin >> x) is interpreted as true if it read x successfully and is interpreted as false otherwise.
1.23 12 -3.1416 4.321 Chavo 1.23 12 -3.14 16 4.321
vector<double> read() { vector<double> numbers; int newEntry; while ( cin >> newEntry ) numbers.push_back(entry); return numbers;}
Example: appending vectors(c.f. overloading of + in string)
○ Ex: Write a function which appends vector v2 (int) onto the back end of vector v1.
void append(vector<double>& v1, vector<double> v2){ for ( int i=0 ; i<v2.size() ; i++ ) v1.push_back( v2[i] ); return;}
○ Why is v1 passed by reference and v2 by value?○ What would change if we pass v2 by reference?
v2
1 0
resulting vector v1
6 3 -8 4 1 0
v1 by reference
6 3 -8 4
Vector member functions● Class: vector<data_t>
data_t could be int, double, string, Card, etc.
Member function Description
vector(int n) Constructs a vector with n elements.
int size() Returns current size of vector
void push_back(data_t d) inserts d at the back of the vector
void pop_back() Removes last element of the vector.
void resize(int n) Resizes vector to size n. If n is less than the old size, elements at the back are deleted.
Strings as vectors● A string is a vector of char elements
string name = "Ramon";name[0] = 'J';cout << name; // Prints Jamon (Ham, yummy!)
● A string variable can use vector member functions.name.push_back('s'); //Adds one char to the end.
● Say you had to replace every s in a string with an f.○ You could extract the single letter 's' with substring,
then erase it and finally insert the 'f'.○ Or you could use vector notation
for ( int i=0 ; i < myString.length() ; i++ ) { if ( myString[i] == 's' ) myString[i] = 'f';}
Conserving memory● Every time a variable is passed by value to a
function, a local copy of the variable is created.○ If a vector storing one million integers is passed by value,
a new vector of size one million is created.○ This eats up memory and can crash your program.
● In these cases consider passing a vector by reference.○ But remember that changes will be recorded
■ You can either be super careful.■ Or you can 'add insurance' to prevent accidental changes.
● To 'add insurance', use the const modifiervoid myFunction( const vector<int>& myVector )
Example: searching a list○ Ex: Search through a vector for a specific value and return the
position of the first occurrence.
int findValue(const vector<int>& v, int x){ int i=0; while( i<v.size() && v[i] != x ) i++; return i;}
○ What does this return if x was not in the vector v?
v by reference with const to prevent changes
6 -5 13 7 0
0 1 2 3 4
int pos = findValue( v , 7 ); // pos =
returns 3
3
Example: erasing an element○ Ex: Erase from a vector v (int) the element stored at a given
position.
void erase(vector<double>& v, int pos) { if ( pos>=0 && pos<v.size() ){ for (int i=pos ; i < v.size() - 1 ; i++) v[i] = v[i+1]; v.pop_back(); }}
○ What happens if pos equals v.size()-1?
v by reference to record changes
6 -5 13 7 0
0 1 2 3 4
erase(v,2); v
6 -5 7 0
0 1 2 3
Example: inserting an element○ Ex Insert a double x before a given position in a double vector
v: insert(v,2,x);
You do it...
(6.4) Parallel vectors
Say we want to create a phonebook app.● We would need to keep track of different individual data
types like○ name (string name)○ phone number (int phone)○ friend score!!! (int score)
● Friendships are dynamic… ● We need resizable containers. ● We can use parallel vectors.
name
Ramon
Chavo
Quico
Chilindrina
Popis
phone
1234567
8901234
2223344
9876543
5555554
0
1
2
3
4
score
8
10
5
7
3
vector<string> name(5);vector<int> phone(5);vector<int> score(5);
● But it is hard work keeping track of 3 separate vectors.
Parallel vectors (cont)
But… ● it is hard work keeping track of
3 separate vectors.
phone
1234567
8901234
2223344
9876543
5555554
// Popis is no longer our friend. // Remove record using functions// erase(vector<T>& ,int )
name
Ramon
Chavo
Popis
Chilindrina
Quico
score
8
10
3
7
5
0
1
2
3
4
name
Ramon
Chavo
Chilindrina
Quico
phone
1234567
8901234
9876543
5555554
score
8
10
3
7
5
erase(name,2);erase(phone,2);
// What if we make mistakes?// erase(score,2);
(6.4) Parallel vectors● A better solution is to create a class Friend with
private variables name, phone and score.● Then create just one vector<Friends> object.class Friend { public: . . . private: string name; int phone; int score;}. . . int main(){ vector<Friend> myFriends(5); . . . erase(myFriends,3); . . .}
Friend
Friend
Friend
Friend
Sorting a vector (integers)● The <algorithm> library contains a function that is
capable of sorting a vector or an array.● The function is called sort.
○ A typical call has the form:sort( iteratorToBeginningOfVector , iteratorToEndOfVector );
○ We will explain what iterators are at a later time…
● For now we only need to know that○ the vector member function begin() returns an iterator to
the beginning of the vector,○ and that the vector member function end() returns an
iterator to the end of the vector.
Sorting (cont)The code below sorts and prints a vector of integers:
#include <iostream>#include <vector>#include <algorithm>using namespace std;
int main(){ vector<int> v(4); v[0]=25; v[1]=2; v[2]=13; v[3]=9;
sort( v.begin() , v.end() ); for ( int i=0 ; i < v.size() ; i++ ) cout << v[i] << " "; cout << endl; return 0;}
Output: 2 9 13 25
Sorting a vector (other types)● We can also sort double and string…
○ It looks a little strange because strings are sorted in lexicographic order.
● Can we sort Point objects?○ No. The compiler doesn’t know how to compare points.
● Can we sort Product objects?○ Yes. Recall we have the member function:
Product::operator < (Product b);
1.23 0.21 34.1 -10 17.2 -10 0.21 1.23 17.2 34.1sort
chavo ramon 8 led ovahc CHAVO
8 led ovahc CHAVO chavo ramonsort
Sorting 'naipes' (cards)○ Ex: Create and sort a 13 cards out of a deck.
■ We need the operator <. Write the implementation. I'll wait!
vector<Card> deck(13);for ( int i=0 ; i<deck.size() ; i++ ) { Card newCard; deck[i] = newCard;}
sort( deck.begin() , deck.end() );for ( int i=0 ; i<deck.size() ; i++ ) { cout << deck[i].get_rank() << " of " << deck[i].get_suit() << endl;}
○ Note this sorts on the rank, but the suits are in random order.
Everyday I'm shuffling… Ex: Shuffle a deck of 52 cards.
○ We want to call a function shuffle like this:
vector<Card> deck(52);shuffle(deck);
○ Idea: Pick 2 random cards from the deck and swap their positions. ■ Repeat many times.
○ Let’s do 2018 swaps.
void shuffle(vector<Card>& v){
Card temp; for (int i=1; i<=2016; i++){ int pos1 = rand()%52; int pos2 = rand()%52;
temp = v[pos1]; v[pos1] = v[pos2]; v[pos2] = temp; } return;}
Quick summary● A vector is a class that can store data in a list.● We set the type and initial size in the declaration.
vector<string> listOfWords(7);vector<int> myVector(314);
● Request the size of a vector with .size()int totalWords = listOfwords.size();
● To access an element use brackets: []● Indices start at 0.
listOfWords[0] = "hola"; cout << myVector[1] + myVector[2];
● Vectors are dynamic.listOfWords.resize(5); // First 5 elements survivelistOfWords.push_back("Ramon"); // Add "Ramon" to endlistOfWords.pop_back(); // Remove last element
2D Vectors (tables or matrices)● Creating a 'table' is easy with arrays (more on this later).
int myMatrix[2][4]; // Creates an empty 2x4 matrix
● Creating it with vectors is not so easy.○ Method 1: Create a vector of size rows*cols and keep track of
the rows and columns yourself… /* Creates a "Matrix" with 8 elements */vector<int> myFakeMatrix(8);
○ Method 2: Create a vector of vectors./* Creates an empty 2x4 matrix */vector< vector<int> > VectorOfRows(2); vector<int> row(4);for ( int i=0 ; i<VectorOfRows.size() ; i++ ) vectorOfRows[i] = row;
● For two dimensional data it's better to use arrays.