-
Notifications
You must be signed in to change notification settings - Fork 0
STL Algorithms
Inspect the code brought to your disposal. Currently all Product class objects are read in function productDBRead()
using an old style loop.
Use the newly learned std::istream_iterator
to read the file. (Hint: Use std::back_inserter()
- Why?)
Remember to test and verify
To make the read code more compact, we need to define 2 new istream_iterators
, one for the file itself, and one for end of stream or eos.
After that, std::copy()
can be used to push the database contents into the ProductList vector
void productDBRead(ProductList &pl, const std::string &fileName)
{
pl.clear();
std::ifstream pFile(fileName.c_str());
std::istream_iterator<Product> fileItr(pFile);
std::istream_iterator<Product> eos;
std::copy(fileItr, eos, std::back_inserter(pl));
}
After compilation, we can se that the program does as we expect(Note: product.db needs to have the file path for it to work)
The same old style is used for writing to std::cout
in function printAll()
- update the function
such that it uses std::ostream_iterator
.
Now to use the ostream_iterator
we have to once again use std::copy()
to print to terminal
void printAll(const ProductList &pl)
{
std::cout << "##################################################"
<< std::endl;
std::cout << "Printing out all products..." << std::endl;
std::cout << "----------------------------" << std::endl;
std::ostream_iterator<Product> plItr (std::cout, "\n");
std::copy(pl.begin(), pl.end(), plItr);
std::cout << "##################################################"
<< std::endl;
}
The output looks like this:
Currently we can only read from the file and print out the contents. Next thing to do would
naturally be to add an item.
Add the missing code in addItem()
In our addItem()
fuction, we ask for the name, price and quantity. That information we use to make a Product
object, after that its simply pushed onto the ProductList
Vector
void addItem(ProductList &pl)
{
std::string productName;
float price;
unsigned int sold;
std::cout << "Please enter name for new Item: ";
std::cin >> productName;
std::cout << std::endl << "Please enter price for: " << productName << " ";
std::cin >> price;
std::cout << std::endl << "Please enter quantity sold: ";
std::cin >> sold;
Product P(productName, price, sold);
pl.push_back(P);
}
Having performed changes to the product list, we need to be able to save it for future use.
Use std::ostream_iterator
in productDBWrite()
to perform the deed.
For adding items just simply make an ofstream
and use std::copy()
to write into the database
/**
Write data to db file
*/
void productDBWrite(const ProductList &pl, const std::string &fileName)
{
std::ofstream pFile(fileName.c_str());
std::copy(pl.begin(), pl.end(), std::ostream_iterator<Product>(pFile, "\n"));
}
Pictured above we can see that the database has been updated with the product FredRokk
Inspecting the product list it becomes evident that not all products are equally highly valued.
In this trial of times we wish only to have products in stock that actually sell well.
Complete the function printPoorlySellingProducts()
with a printout that only contains those
products that have sold fewer than 10 in all.
Use std::remove_copy_if()
for copying those that are desired and std::ostream_iterator
for
writing to std::cout
.
To get the poorly selling items, we simply have to use std::remove_copy_if()
to remove the items that ARE selling. As the last argument of std::remove_copy_if()
we need to make a lambda function, a function that is only defined in this functioncall. Lambdas don't need to be declared before use, its therefore its good to use in this context.
/**
* Print poorly selling products
*/
void printPoorlySellingProducts(const ProductList &pl)
{
std::cout << "##################################################"
<< std::endl;
std::cout << "Printing out bad selling products..." << std::endl;
std::cout << "----------------------------" << std::endl;
std::remove_copy_if(pl.begin(), pl.end(),
std::ostream_iterator<Product>(std::cout, "\n"),
[](Product p) { return p.sold() >= 9; });
std::cout << "##################################################"
<< std::endl;
}
As we can see the function does what it is meant to.
In the following two exercises a discount is set on the products using two different approaches.
Use std::for_each()
and set a new price on each product based on a discount of 10%.
Again to try different solutions, two are sought.
- Using a functor, where the overloaded function operator has the following signature:
void operator()(Product& p)
- Using a lambda
Implement your solution(s) are to be implemented in
addDiscountUsingForEach()
. Test each and verify that they work.
In this exercise were using a lambda, since thats the most simple to use.
We use a std::for_each()
to discount every item by 10%, then the lambda, takes the price and multiply it by 0.9, since thats the same as subtracting 10%.
/**
* Set a discount on all products - Using for_each()
*/
void addDiscountUsingForEach(ProductList &pl)
{
std::for_each(pl.begin(), pl.end(),
[](Product &p) { p.setPrice(p.price() * 0.9); });
}
the prices of the products on the database is:
Highland_Park 200 23
Glenlivet 150 12
Chivas_Regal 100 5
FredRokk 69 55
test 19 10
As you can see, the price has been lowered by 10%
Having tried using std::for_each()
lets try using std::transform()
. The idea in this exercise
is to pass the (1) product list, (2) a discount calculating lambda and (3) std::ostream_iterator
for printing out directly.
The discount, itself, must supplied by the user via std::cin
. This implies that the lambda we
are to use is a bit different this time. It should be lambda returning a lambda. The first taking
the discount supplied by the user.
Furthermore use std::clamp
to ensure that the given percentage is between 10 and 90.
Implement your solution in addDiscountUsingTransform()
In this exercise we wont be using the lambda that returns a lambda, since the implementation is hard to do, and not a lot of information can be found on the web.
std::transform()
is defriend from std::for_each()
since the lambda function needs to return the Product object.
/**
* Set a discount on all products - Using transform()
*/
void addDiscountUsingTransform(ProductList &pl)
{
std::vector<Product> tempVec(pl.size());
std::copy(pl.begin(), pl.end(), std::back_inserter(tempVec));
std::transform(pl.begin(), pl.end(), tempVec.begin(), [](Product &p) {
int discount;
std::cout << "Pleace inter discount percentage: ";
std::cin >> discount;
discount = clamp(discount, 10, 90);
float multiplier = (100 - (float)discount) / 100;
float price_f = (p.price() * multiplier);
p.setPrice(price_f);
return p;
});
}
Just like last time this implementation works
An interesting metric could be the total amount of products sold. To calculate this we will be using the version of std::accumulate()
that takes a binary operation as it’s last parameter.
How should the lambda be formed in order for this to work?
std::accumulate()
need the parameters first and last iterater, an initial number to start counting on, and a lambda function to know what to do with the Product objects. The lambda takes two arguments, the first will be an int refrence that carries the current sum, and the Product object reference.
/**
* Calculate the total amount of sold products
*/
void calcTotalSoldProducts(ProductList &pl)
{
std::cout << "##################################################"
<< std::endl;
std::cout << "Total amount of products sold: "
<< std::accumulate(
pl.begin(), pl.end(), 0,
[](int &last, Product &p) { return last + p.sold(); })
<< std::endl;
std::cout << "----------------------------" << std::endl;
}
The current sum of sold products in the database is 105, and the output after a test is:
Discuss the pros and cons of using algorithms. E.g. In your opinion does the use improve clarity or does it add to the clutter? Include code snippets to substantiate your stance.
The std::
iterator functions are a really powerful tool to weald, since it automates some basic operations, that probably are useful for some applications. Also it is clear to see by these operations what is happening instead of forloops that perform the same work. Lastly, it is nice to have less code overall, to not clutter the code.
DOESN'T APPLY SINCE WE DIDN'T DO THE TEMPLATE LAB CHALLANGE!
NOT DONE SINCE IT'S OPTIONAL
Made by: Frederik Both Rokkjær, Frederik Kronvang Gade and Christian Olsen