简体   繁体   中英

Round function doesn't seem to work properly

C++

The job is to find the average length of series of strings.

I used this code and it does work for 1st,2nd and 4th test cases (out of total 5)( ps the inputs are unknown)

#include <cmath>
#include <iostream>

using namespace std;

int main() {
  string input;
  int k = 0;
  int sum = 0;

  while (cin >> input) {
    int n = input.size();
    k++;
    sum += n;
  }

  float out = (float)sum / k;
  cout << (int)round(out);

  return 0;
}

When I use cout<<round(out); It gives the same result.

When I use cout<<ceil(out); The last 4 cases are correct and the first one fails.

When I use cout<<floor(out); Only the 1st case is correct and all else fail.

What am I missing?

The question

You are in a college level English class, your professor wants you to write an essay, but you need to find out the average length of all the words you use. It is up to you to figure out how long each word is and to average it out. Task: Takes in a string, figure out the average length of all the words and return a number representing the average length. Remove all punctuation. Round up to the nearest whole number.

Input Format: A string containing multiple words.

Output Format: A number representing the average length of each word, rounded up to the nearest whole number.

Sample Input: this phrase has multiple words

Sample Output: 6

Test case 1 Can you not do that?

Answer 3

Test case 2 The longest word in the dictionary is...

Answer 5

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.

I suspect you are getting tripped up on a corner-case like a string of all punctuation that goes unhandled leading to a floating point exception or something similar, like ",,, === !!!" .

You can handle the case (and all cases) in a number of different ways. A direct approach is using the erase-remove of all punctuation and then counting words and characters by creating a stringstream and simply summing both characters and words. No need for a vector to save the words, you just care about the totals with punctuation and spaces removed, eg

#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
#include <algorithm>

int main (void) {

    std::string line {};
    size_t nchars = 0, nwords = 0;
    
    if (!getline (std::cin, line))                          /* read line of input */
        return 1;
    
    line.erase (std::remove_if (line.begin(),               /* erase-remove punct */
                                line.end(),
                                [](unsigned char c) {
                                    return ispunct(c);
                                }), line.end());
    
    if (line.find_first_not_of(" \t") == std::string::npos) {   /* all punct/space */
        std::cout << "avg: 0\n";
        return 0;
    }
    
    std::stringstream ss (line);                            /* stringstream from line */
    while (ss >> line) {                                    /* count words */
        nchars += line.length();                            /* add total chars */
        nwords += 1;                                        /* add total words */
    }
    
    std::cout << "avg: " << (nchars + nwords - 1)/nwords << '\n';   /* result */
}

Example Use/Output

$ ./bin/mean_of_word_len
this phrase has multiple words
avg: 6

or

$ ./bin/mean_of_word_len
Can you not do that?
avg: 3

or

$ ./bin/mean_of_word_len
The longest word in the dictionary is...
avg: 5

or

$ ./bin/mean_of_word_len
The longest word in the dictionary is......................................
avg: 5

or

$ ./bin/mean_of_word_len
,,, === !!!
avg: 0

or

$ ./bin/mean_of_word_len
,,, a === !!!
avg: 1

Look things over, give it a try, and let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM