Without seeing example inputs, I'm just guessing at the format. When trying to run your code in my editor, I get an infinite loop. This is because std::cin
doesn't convert to false
if you just press Enter (Empty string). So my code makes the assumption that all the words are entered on a single line. Which means I now have to split the line into individual words.
To make it work with std::cin
, I'd have to press Ctrl+d
(WSL Debian) to break out of the loop, and I don't like that at all.
The biggest issue you have is that you don't strip punctuation. If you don't do that, the punctuation marks add to your string length and will throw your average off. You also read "Round up the nearest whole number" as simply rounding, which can round up or down. The function you want is std::ceil()
, as it will always go up to the next whole number.
#include <algorithm> // std::remove_if, std::copy
#include <cctype> // std::isalpha
#include <cmath>
#include <iostream>
#include <iterator> // std::istream_iterator
#include <numeric> // std::accumulate
#include <sstream> // for easier (in code) string split on whitespace
#include <vector> // Dynamic array for holding individual words
// Splits whole line string into vector of individual words
std::vector<std::string> split(std::string line) {
std::vector<std::string> tmp;
std::stringstream stream(line);
std::copy(std::istream_iterator<std::string>(stream),
std::istream_iterator<std::string>(), std::back_inserter(tmp));
return tmp;
}
// Strips non-alphabetic characters from each word in a vector of words
std::vector<std::string> strip(std::vector<std::string> words) {
for (auto& word : words) {
word.erase(std::remove_if(word.begin(), word.end(),
[](const auto& x) { return !std::isalpha(x); }),
word.end());
}
return words;
}
int main() {
std::string input;
std::getline(std::cin, input); // Reads a whole line of input until Enter
auto words = split(input);
words = strip(words);
if (words.size() == 0) {
std::cout << 0;
return 0;
}
// Once words is cleaned up, it's possible to get the mean directly
std::cout << static_cast<int>(std::ceil(std::accumulate(
words.begin(), words.end(), 0.0,
[numWords = words.size()](const auto& mean, const auto& x) {
return mean + (static_cast<double>(x.length()) / numWords);
})));
// Or the mean can still be calculated in a more manual fashion
// int sum = 0;
// for (auto i : words) {
// sum += i.length();
// }
// int mean =
// static_cast<int>(std::ceil(static_cast<double>(sum) / words.size()));
// std::cout << mean;
return 0;
}
Inputs (separate run per line):
I want to write something
I() want, to. write*&^%$#@! something?
Output for both inputs is 5, which should be correct according to your requirements.
The main thing you will want is the strip()
function.
I break the work up into a few functions, which makes the work in the main function easier to follow. I use a "one-liner" to directly calculate the mean and then print it, but I left a more manual approach in the comments. Some extra verbosity is added by my explicitly casting the double
returned from std::ceil()
to an int
. Prefer static_cast
over the C-style cast, it produces safer code.
Because I'm using Standard Library functions and constructs where (mostly) possible, I'm also making heavy use of lambdas, the things that generally have the form []() {}
. Understanding lambdas unlocks a lot of the Standard Library for use, and I highly recommend not putting the subject off for very long.
If not for the needs to strip out punctuation and round up, the mean calculation would be a lot cleaner.
It's possible that the structure of the input can simplify the code quite a bit, but it's also possible that your learning source simply didn't properly prepare you for how much effort string parsing usually involves.