Reputation: 41
Currently working on an exercise from Stroustrup's intro to C++ book and it asks: Write a function that given two vectors price and weight computes a value (an “index”) that is the sum of all price[i]*weight[i]. Make sure to have weight.size()==price.size()
vector<double> weight;
vector<double> price;
void read_weight() {
cout << "Input weight" << '\n';
for (double weights; cin >> weights; ) {
weight.push_back(weights);
if (!(cin >> weights)) {
cin.clear();
cin.ignore(numeric_limits<double>::max(), '\n' );
break;
}
}
}
void read_price() {
cout << "Input price" << '\n';
for (double prices; cin >> prices; ) {
price.push_back(prices);
if (!( cin >> prices)) {
cin.clear();
cin.ignore( numeric_limits<double>::max(), '\n' );
break;
}
}
}
void calculate() {
double sum = 0;
for (int i = 0; i < price.size(); ++i)
sum += price[i] * weight[i];
cout << "Sum is " << sum << '\n';
}
int main() {
read_weight();
read_price();
calculate();
}
This is the code I currently have, but I cannot figure out how to terminate input properly. In previous chapters, his book mentions you could use an input besides a double to terminate (like |). However, I learned that this only puts cin into the failed state and does not allow me to move onto the price function. Unfortunately, the book does not cover how to handle this as of yet so I just copied code about cin.clear and cin.ignore in hopes that it would work. But this did nothing. I noticed that I was actually allowed a single input if I were to change the vector to int instead of double and am perplexed for the difference in behavior. I was wondering if anyone could give me tips on how to fix this?
Upvotes: 0
Views: 397
Reputation: 498
You can use std::getline to read a whole line from cin into a string. Thereafter, you need to determine whether the line that has been entered is a double or not. I normally wouldn't recommend going too wild with regular expressions (I don't use them a lot in C++), but in this case I reckon it is a pretty simple and sound solution. If the string is in the format "32.23" (digits dot digits), you convert it to a double using std::stod, push that to the vector and continue to read from cin. If it is not, you break the loop and go on with the program flow.
Stay away from using global variables, use local ones and pass them arround.
Also, notice that your functions read_price and read_weight are almost identical. In such a case, you definitely only want to write a single (parameterized) function. In this case, you don't even need parameters, you can just use the same function for both.
(You could also directly read your values into double variables from a stream (std::cin), which might be considered more elegant as you need less conversion, however the method below is easy and you don't need to worry what is being entered into std::cin)
#include <vector>
#include <string>
#include <iostream>
#include <regex>
std::vector<double> get_doubles_from_cin(){
std::vector<double> doubles;
std::regex double_regex ("\\d+\\.\\d+");
std::string input;
while(std::getline(std::cin, input)){
if (std::regex_match(input, double_regex)){
doubles.push_back(std::stod(input));
}
else{
break;
}
}
return doubles;
}
void calculate(std::vector<double>& weights, std::vector<double>& prices) {
double sum = 0;
for (int i = 0; i < prices.size(); ++i) {
sum += weights[i] * prices[i];
}
std::cout << "Sum is " << sum << '\n';
}
int main() {
std::cout << "Enter weights" << std::endl;
auto weights = get_doubles_from_cin();
std::cout << "Enter prices" << std::endl;
auto prices = get_doubles_from_cin();
calculate(weights, prices);
}
Upvotes: 2
Reputation: 70372
You were halfway there already. I've written a commented replacement for read_weights()
; you should be able to take it from there.
#include <vector>
#include <limits>
#include <iostream>
// Use return values instead of working on global variables.
// Avoid using global variables whenever possible, they are a
// maintenance pain.
std::vector< double > read_weights()
{
std::vector< double > weights;
double weight;
// You can embed the \n right in the string, no need to put it as a
// separate character.
std::cout << "Input weights; enter a non-number to terminate input.\n";
// If anything not double is entered, cin goes into fail state --
// that is our terminating condition right there, so use it!
while ( std::cin >> weight )
{
weights.push_back( weight );
}
// Clean up cin
std::cin.clear();
// Use the correct type for max(); you had 'double' here...
cin.ignore( numeric_limits< std::streamsize >::max(), '\n' );
// Don't worry about the apparent copying of the vector upon return.
// Any modern compiler should be able to optimize this away.
return weigths;
}
A simple main() for testing:
int main()
{
std::vector< double > weights = read_weights();
std::cout << "Vector contents:\n";
for ( auto & v : weights )
{
std::cout << v << "\n";
}
}
Now all you have to add is a read_price()
... now wait, you don't, do you? Because all you're actually doing is the very same thing as in read_weights()
, entering doubles! So move the input prompt out of read_weights()
and make it one function, read_values()
, which you call twice, once to get weights
and once to get prices
...
int main()
{
std::cout << "Enter weights; enter a non-number to terminate input.\n";
std::vector< double > weights = read_values();
std::cout << "Enter prices; enter a non-number to terminate input.\n";
std::vector< double > prices = read_values();
// ...
}
For the calculate
function, use references for the parameters so the vectors don't have to be copied:
void calculate( std::vector<double> & weights, std::vector<double> & prices )
And once you got this all running, keep in your mind that, later on, you will (or at least should) be learning about <algorithm>
, functors, and lambdas... which should remove the need for calculate
and replace it with an elegant one-liner... but that's yet to come, and I don't want to confuse you with that at this point.
Upvotes: 2