-2

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)( p.s. 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

  • what happened when you tried test cases for which you do know input and output? – 463035818_is_not_an_ai Jan 20 '21 at 14:50
  • 1
    Did you try `cout << out;` ? – Damien Jan 20 '21 at 14:52
  • 1
    I bet that if you had 100 test cases, one or more will fail regardless of what contortions you find yourself doing to get the rounding to work. [Is floating point math broken](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – PaulMcKenzie Jan 20 '21 at 14:54
  • I wouldn't mind a link to the problem. Rounding or taking the ceiling or floor will naturally change your average to a whole number, which will rarely be correct. It could be your use of floats like Paul suggests (likely), or some requirement you glossed over. – sweenish Jan 20 '21 at 15:00
  • @largest_prime_is_463035818 the known inputs gives correct output... –  Jan 20 '21 at 15:12
  • @Damien it requires output rounded to the nearest whole number –  Jan 20 '21 at 15:13
  • @PaulMcKenzie not gonna lie I've been thinking the same for the last two days...getting the same problem thrice in 3 days. –  Jan 20 '21 at 15:15
  • 2
    @Janstew You should include in the post (and not in a comment) the exact wording of the question. For example, if `out == 2.5`, what should be the result printed? – Damien Jan 20 '21 at 15:15
  • @sweenish it's on an android app.. https://www.sololearn.com/coach/73?ref=app ... It needs the average rounded to the nearest whole number. –  Jan 20 '21 at 15:17
  • In that case, I'm with Damien, and the entire text of the problem should be in the question. The link you provided is just a 404. – sweenish Jan 20 '21 at 15:22
  • @Janstew -- What if the average is `3.499900000001`? Is the answer 3, or 4? What if this is the result the program gave, but a hand (not computer) calculation had the value at `3.5`? – PaulMcKenzie Jan 20 '21 at 15:22
  • @PaulMcKenzie yeah I'm getting what you're saying. –  Jan 20 '21 at 15:32
  • What if the string is empty? That will try to compute 0/0 which will fail, even though the output should be 0 – feverdreme Jan 20 '21 at 17:22
  • 1
    @Janstew The question as listed in your post says to round *up*. It also says to remove punctuation, and I don't see any attempt to do that in your code. – Dave Costa Jan 20 '21 at 18:04
  • @DaveCosta nope see the attached cases and answers. –  Jan 21 '21 at 01:27
  • Unless the file is really huge, you would never have issues with floating points with this code as you do all additions in intergers and a single division.As noted by others, the problem is that you ignore some requirements. Obviously, if you use wrong rounding, you would have a lot of failures. For punctuation, I would expect the ration to be lower assuming English sentences in input file. – Phil1970 Jan 21 '21 at 03:22
  • I did some manipulation here and there to check the issue and turns out what @PaulMcKenzie said was absolutely what's going on here. –  Jan 21 '21 at 04:31

2 Answers2

1

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.

sweenish
  • 4,793
  • 3
  • 12
  • 23
  • Many good advices but overly complex given that you could mainly replace `input.size()` in original code by `std::count_if` *(and do a few minor adjustments to code)*. – Phil1970 Jan 21 '21 at 03:41
  • `std::count_if` is a good suggestion. I will keep that one in mind. – sweenish Jan 21 '21 at 03:55
1

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, e.g.

#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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Nope... Read the 2nd test case which I attached... The punctuation characters are counted as well. –  Jan 21 '21 at 04:27
  • @Janstew but your question explicitly says ***"Remove all punctuation. Round up to the nearest whole number."*** ?? Is that not right? – David C. Rankin Jan 21 '21 at 06:35
  • sry I got to know that that platform's questions are messed up... And is not moderated. I stopped using it. So never mind about this question . –  Jan 21 '21 at 12:35