开发者

Print a Histogram based on word lengths (C)

This is a K&R exercise (1-13)...

"Write a program to print a histogram of the length of words in its in开发者_如何学运维put. It is easy to draw the histogram with bars horizontal; a vertical orientation is more challenging."

The section was about arrays, and to be honest, I'm not sure I fully understood it. Everything up to this point was fairly easy to grasp, this was not.

Anyway I'm trying to do a histogram with horizontal bars first. Once I got that down I'll try vertical, but right now I'm not even sure where to begin with the easy version. (I slept on it, woke up, and still couldn't get it.)

I drew an example of what the program would output:

----------------------------------------------------------------
001|XX
002|XXXX
003|X
004|XXXXXXXXXX
005|XXXXXXXXXXXXXXXXXXXXXXXXX
006|XXXX
007|X
008|
009|XXXXXXXXX
010|XXX
>10|XXXX
----------------------------------------------------------------

And tried to break it (the program) down in sections. This is what I came up with:

  1. PRINT TOP BORDER
  2. PRINT CATEGORY, PRINT X EACH TIME CONDITION IS TRUE, PRINT NEWLINE, REPEAT.
  3. PRINT BOTTOM BORDER

But the more I think about it the less I think that's how it would work (because getchar() goes through one character at a time, and it wouldn't be able to go back up to put a X in the right category.) Or...

... I'm just really confused as to how I would solve this problem. Here's as far as I've been able to get code wise:

#include <stdio.h>

#define MAXWORDLENGTH 10

// print a histogram of the length of words in input. horizontal bar version

int main(void)
{
  int c;
  while ((c = getchar()) != EOF) {

  }

  return 0;
}

Could someone help enlighten me? Not necessarily with the code, maybe just pseudo code, or with some "words from the wise" as to what I need to do, or think, or something. This has just been a really big stone in the road and I'd like to get past it :/.

(I'll check back in 30 minutes)


I loved the pseudo-code! Some good thinking there, but you're still not ordering your program right.

As you said yourself, you can't read the text, go back and print an X in a particular row. If we establish that it can't be done, then there's no choice but to know all the values of the histogram beforehand.

So you should think your program as having two parts (and you'll make this kind of division in practically every program you write): first, a part that will make calculations; and then a part that will output them in a certain format (the histogram).

This tip should get you started! If you need further help, comment below.


I suggest you simplify the problem by solving it for the case of one word per line, so you can use fgets. Here's how to "eat up" lines that are too long.

Then, as often, the central data structure is the key to solving the problem. The data structure you need is an array used as frequency table:

int freq[11];

In freq[1], store the number of words/lines of length 1, in freq[2] those of length 2, etc., and in freq[0] those of length >10. You don't need to store the words since the rest of the program only needs their length. Writing out the histogram should be easy now.

I hope this isn't too much of a spoiler.


The code below prints a horizontal histogram using only the basic toolkit provided by the book so far:

#include<stdio.h>

/* Prints a horizontal histogram of the lengths of words */

#define MAX_WORDS 100
#define IN 1
#define OUT 0

main()
{
 int c, length, wordn, i, j, state, lengths[MAX_WORDS];
 wordn = length = 0;
 state = OUT;
 for (i = 0; i < MAX_WORDS; ++i) lengths[i] = 0;

 while ((c = getchar()) != EOF && wordn < MAX_WORDS)
 {
    if (c == ' ' || c == '\t' || c == '\n')
        state = OUT;

    else if (wordn == 0) 
        {
         state = IN;
         ++wordn;
         ++length;
        }

    else if (state == IN)
         ++length;

    else if (state == OUT)
        {
         lengths[wordn] = length;
         ++wordn;
         length = 1;
         state = IN;
        }
 }    

 lengths[wordn] = length;

 for (i = 1; i <= wordn; ++i)
    {
     printf("%3d: ",i);
     for (j = 0; j < lengths[i]; j++)
        putchar('-');
    putchar('\n');
    }
}


#include<stdio.h>
#define RESET 0
#define ON 1

main()
{
  int i,wnum=0,c,wc[50];
  int count=0,state;
  state=RESET;
  for(i=0;i<50;++i)
    wc[i]=0;
  /*Populating the array with character counts of the typed words*/
  while((c=getchar())!=EOF)
    {
      if(c=='\n'||c=='\t'||c==' '||c=='"')
    {
      if(state!=RESET)
        state=RESET;
    }
      else if((c>=65&&c<=90)||(c>=97&&c<=122))
    {
      if(state==RESET)
        {
          count=RESET;
          ++wnum;
          state=ON;
        }
      ++count;
      wc[wnum-1]=count;
    }
    }
  c=RESET;

  /*Finding the character count of the longest word*/
  for(i=0;i<wnum;++i)
    {
      if(c<wc[i])
    c=wc[i];
    }

  /*Printing the Histogram Finally*/ 
 for(i=c;i>0;--i)
    {
      for(count=0;count<wnum;++count)
    {
      if(wc[count]-i<0)
        printf("  ");
      else printf("x ");
    }
      printf("\n");
    }
}

VERTICAL ORIENTATION: Using only the tools we learned so far in the book. And you can change the array size, wc[50]. I kept the code valid for 50 words. Horizontal orientation should be quite simpler. I didn't try it though.


To histogram the word lengths, you are going to need to know the word lengths.

  • How do you define a word?
  • How can you measure the length of a word? Can you do it one character at a time as you read the stream, or should you buffer the input an use strtok or something similar?

You will need to accumulate data on how many occurrences of each length occur.

  • How will you store this data?

You will need to output the results in a pleasing form. This is fiddly but not hard.


I will link the answer below but since you asked for details the key seems to be this

Use an ARRAY of lengths i.e have an array with each element initialised to zero assume MAX wordlength to be about 30...

*have a flag while in the word and increment a counter every time a whitespace is NOT encountered

*once out of the word flag is set to "out" and the corresponding word length index item in the array is incremented i.e if word length counter is w_ctr use

array[w_ctr]++

*use the array as a table of reference for each line in a loop to print each line in the histogram so you can use the array and will now be able to determine weather the 'X' in the histogram is to be inserted or not

EDIT: sorry i didn't read the question right but the idea is simpler for Vertical histograms and the same thing can be used.

after the last step just print the horizontal histogram until counter exceeds current wordlength being printed

for(ctr=0;ctr<array[current_wordlength];ctr++)
     printf('X');    

End


the original is here http://users.powernet.co.uk/eton/kandr2/krx113.html

CLC-wiki is also a place see the comments for details.


//This is for horizontal histogram.
//It works for any number of lines of words where total words <= MAX
#include <stdio.h>
#define MAX 100 //Change MAX to any value.But dont give words more than MAX.

void main()
{
    int w, nwords[MAX] = {0}, i = 0;   //nwords is an array for storing length of each word.Length of all words initialized to 0.

    while ((w = getchar()) != EOF)
    {
        if (w == ' ' || w == '\t' || w == '\n')
            ++i;            //if space or tab or newline is encountered, then index of array is advanced indicating new word
        else
            ++nwords[i];        //increment the count of number of characters in each word
    }       //After this step,we will have array with each word length.

    for (i = 0; i < MAX; i++)   //iterating through array
    {
        printf("\n");
        for (; nwords[i] > 0; nwords[i]--)  
            printf("$");        //if length of word > 0 , print $ and decrement the length.This is in loop.
        if (nwords[i+1] == 0)   //as MAX is 100, to avoid printing blank new lines in histogram,we check the length of next word.
            break;              //If it is 0, then break the loop
        printf("\n");   //After each word bar in histogram, new line.
    }
    printf("\n");
} //main


I've been also studying K&R book. An good approach is to use a int array for storing word frequencies. The array index is the word length, and the array values are the frequencies.

For example:

int histogram[15]; // declares an int array of size 15

// it is very important to initialize the array
for (int i = 0; i <= 15; ++i) {
    histogram[i] = 0;
}

histogram[4] = 7; // this means that you found 7 words of length 4

Given that now you have a data structure for storing your word length frequencies, you can use the same reasoning of Word Counting example found in the book to populate the histogram array. It is very important that you manage to find the right spot for word length tracking (and resetting it) and histogram update.

You can create a function display_histogram for displaying the histogram afterwards.

Here's a code example:

#include<stdio.h>

#define MAX_WORD_LENGTH 15
#define IS_WORD_SEPARATOR_CHAR(c) (c == '\n' || c ==  ' ' || c == '\t')

#define IN  1
#define OUT 0

/* WARNING: There is no check for MAX_WORD_LENGTH */

void display_horizontal_histogram(int[]);
void display_vertical_histogram(int[]);
void display_histogram(int[], char);

void display_histogram(int histogram[], char type) {
    if (type == 'h') {
        display_horizontal_histogram(histogram);
    } else if (type = 'v') {
        display_vertical_histogram(histogram);
    }
}

void display_horizontal_histogram(int histogram[]) {
    printf("\n");

    //ignoring 0 length words (i = 1)
    for (int i = 1; i <= MAX_WORD_LENGTH; ++i) {
        printf("%2d: ", i);
        for (int j = 0; j < histogram[i]; ++j) {
            printf("*");
        }
        printf("\n");
    }

    printf("\n\n");
}

void display_vertical_histogram(int histogram[]) {
    int i, j,  max = 0;

    // ignoring 0 length words (i = 1)
    for (i = 1; i <= MAX_WORD_LENGTH; ++i) {
        if (histogram[i] > max) {
            max = histogram[i];
        }
    }


    for (i = 1; i <= max; ++i) {
        for (j = 1; j <= MAX_WORD_LENGTH; ++j) {
            if (histogram[j] >= max - i + 1) {
                printf("  * ");
            } else {
                printf("    ");
            }
        }
        printf("\n");
    }

    for (i = 1; i <= MAX_WORD_LENGTH; ++i) {
        printf(" %2d ", i);
    }

    printf("\n\n");
}

int main()
{
    int c, state, word_length;
    int histogram[MAX_WORD_LENGTH + 1];

    for (int i = 0; i <= MAX_WORD_LENGTH; ++i) {
        histogram[i] = 0;
    }

    word_length = 0;
    state = OUT;
    while ((c = getchar()) != EOF) {
        if (IS_WORD_SEPARATOR_CHAR(c)) {
            state = OUT;
            if (word_length != 0) {
                histogram[0]++;
            }
            histogram[word_length]++;
            word_length = 0;
        } else {
            ++word_length;
            if (state == OUT) {
                state = IN;
            }
        }
    }

    if (word_length > 0) {
        histogram[word_length]++;
    }

    display_histogram(histogram, 'h');
    display_histogram(histogram, 'v');
}

Here's an input/output sample:

kaldklasjdksla klsad lask dlsk aklsa lkas  adç  kdlaç kd dklask las kçlasd kas kla sd saçd sak dasças  sad sajçldlsak dklaa slkdals kkçl askd lsak lçsakç lsak lsak laskjl sa jkskjd aslld jslkjsak dalk sdlk jsalk askl jdsj dslk salkoihdioa slk sahoi hdaklshd alsh lcklakldjsalkd salk j  sdklald jskal dsakldaksl daslk

 1: *
 2: ***
 3: ******
 4: ***************
 5: **********
 6: ****
 7: ****
 8: ***
 9: 
10: *
11: **
12: 
13: 
14: **
15: 


              *                                             
              *                                             
              *                                             
              *                                             
              *                                             
              *   *                                         
              *   *                                         
              *   *                                         
              *   *                                         
          *   *   *                                         
          *   *   *                                         
          *   *   *   *   *                                 
      *   *   *   *   *   *   *                             
      *   *   *   *   *   *   *           *           *     
  *   *   *   *   *   *   *   *       *   *           *     
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 


You should separate your 2 problems in functions, like:

void gethist(char *s, int *hist, int len)
{ /* words here breaks on spaces (' ') */
  char *t;
  for( t=strtok(s," ");t;t=strtok(0," ") )
    if(*t)
      hist[ strlen(t)>len-1?len-1:strlen(t)-1 ]++;
}

void outhist(int *hist, int len)
{
  int i;
  for( i=1; i<=len; ++i )
  {
    char *s = calloc(1,5+hist[i-1]);
    sprintf(s,"%03d|", i);
    memset( s+4, 'X', hist[i-1]);
    puts(s);
    free(s);
  }
}

then its easy in your main:

int main(void)
{
  int c, hist[11] = {};

  char *s = calloc(1,1);
  while ((c = getchar()) != EOF) {
    s = realloc( s, 2+strlen(s) );
    s[ strlen(s)+1 ] = 0;
    s[ strlen(s) ] = c;
  }

  gethist(s,hist,11); free(s);
  outhist(hist,11);

  return 0;
}


The vertical histogram can be printed one line at a time, by going through the array of word lengths and decreasing the word length at each iteration. A # is printed if the word length is still above zero, and a space is printed when it reaches 0. The newline is printed after each iteration.

If lengths[i] holds the number of characters for word i, and wordn is the total number of words, then the following will print the vertical histogram:

#define YES 1
#define NO 0


 more_lines = YES;
 while (more_lines)
 {
  more_lines = NO; 
  for (i = 1; i <= wordn; ++i)
          {
        if (lengths[i] > 0 ) 
                {
                  more_lines = YES;
                  printf("#\t");
                  --lengths[i];
                }
        else 
                 printf(" \t"); 
      }
  putchar('\n');
 }

The complete code is below:

#include<stdio.h>
/* Prints a histogram of the lenghts of words */

#define MAX_WORDS 100
#define IN 1
#define OUT 0

#define YES 1
#define NO 0

main()
{
 int c, length, wordn, i, j, state, more_lines, lengths[MAX_WORDS];
 wordn = length = 0;
 state = OUT;
 for (i = 0; i < MAX_WORDS; ++i) lengths[i] = 0;

 while ((c = getchar()) != EOF && wordn < MAX_WORDS)
 {
    if (c == ' ' || c == '\t' || c == '\n')
        state = OUT;

    else if (wordn == 0) 
        {
         state = IN;
         ++wordn;
         ++length;
        }

    else if (state == IN)
         ++length;

    else if (state == OUT)
        {
         lengths[wordn] = length;
         ++wordn;
         length = 1;
         state = IN;
        }
 }    

 lengths[wordn] = length;

/* Print histogram header */
  for (i = 1; i <= wordn; ++i)    
printf ("%d\t", i);
  putchar('\n');

 more_lines = YES;
 while (more_lines)
 {
  more_lines = NO; 
  for (i = 1; i <= wordn; ++i)
      {
    if (lengths[i] > 0 ) 
        {
          more_lines = YES;
          printf("#\t");
          --lengths[i];
        }
    else 
         printf(" \t"); 
      }
  putchar('\n');
 }
}


Although the exercise is based on Arrays, I tried to write it using the basic while loop and an if statement. I am not really good with Arrays as of now, so thought of trying this out. I have not tested it for bugs though, but it seems to be working fine for most inputs.

    #include<stdio.h>   
    main() {
    long int c;         

    while((c=getchar())!=EOF) { 
        if(c!=' '&&c!='\n'&&c!='\t') {
            putchar("*");
        }

        if(c==' '||c=='\n'||c=='\t') {   
            putchar('\n');   
        } 

    }
    return 0;
    }

Please note that this is a very basic piece of code to print it horizontally, just for the basic understanding of the structure.


// Histogram to print the length of words in its input
#include <stdio.h>
main()
{
    int wordcount[10],c,token=0;
    int word=0, count =0;
    for (int i=0; i<10; i++)
    {
        wordcount[i]=0;
    }

    while((c=getchar())!=EOF)
    {
     if(c== ' ' || c == '\n' || c== '\t')
     {
         // add the length of word in the appropriate array number 
         switch(word)
         {
            case 1:
            ++wordcount[0];break;
            case 2:
            ++wordcount[1];break;
            case 3:
            ++wordcount[2];break;
            case 4:
            ++wordcount[3];break;
            case 5:
            ++wordcount[4];break;
            case 6:
            ++wordcount[5];break;
            case 7:
            ++wordcount[6];break;
            case 8:
            ++wordcount[7];break;
            case 9:
            ++wordcount[8];break;
            case 10:
            ++wordcount[9];break;
         }
         word =0;
     }
     else if (c != ' ' || c != '\n' || c!= '\t')
     {
         word++;
     }

}
    for (int j=0; j<10; j++)
    {
        if(wordcount[j]==0)
        {
            printf("- ");
        }
        for (int k=0;k<wordcount[j];k++)
        printf("X", wordcount[j]);
        printf("\n");
    }


}


Here is the example of simple vertical Histogram

#include <stdio.h>

int main()
{
    int c, i, j, max;
    int ndigit[10];

    for (i = 0; i < 10; i++)
        ndigit[i] = 0;

    while ((c = getchar()) != EOF)
        if (c >= '0' && c <= '9')
            ++ndigit[c-'0'];

    max = ndigit[0];
    for (i = 1; i < 10; ++i)        /* for Y-axis */
        if (max < ndigit[i])
            max = ndigit[i];

    printf("--------------------------------------------------\n");
    for (i = max; i > 0; --i) {
        printf("%.3d|", i);
        for (j = 0; j < 10; ++j)
            (ndigit[j] >= i) ? printf(" X ") : printf("   ");
        printf("\n");
    }
    printf("   ");
    for (i = 0; i < 10; ++i)        /* for X-axis */
        printf("%3d", i);
    printf("\n--------------------------------------------------\n");
    return 0;
}


#include <stdio.h>
#include <string.h>

int main()
{
    //hold length of string
    unsigned long length;
    // Holds the name input by user upto 50 characters
    char name[50];
    //iterator for generating dash for bar chart
    int i = 0;
    //iterator for generating dash for bar chart
    int j = 0;
    //take user name input
    printf("input your name [without spaces and < 50 characters] : ");
    scanf("%s", &name[0]);
    //find the length of string
    length = strlen(name);
    printf("length of your name is %lu \n", length);
    //generate dashes for bar chart
    while (i < length)
    {
        printf("--");
        ++i;
    }
    printf("| \n");
    // fill the bar chart with []
    while (j < length)
    {
        printf("[]");
        ++j;
    }
    printf("| \n");
    //generate dashes for bar chart
    while (length > 0)
    {
        printf("--");
        --length;
    }
    printf("| \n");
}

input your name [without spaces and < 50 characters] : ThisIsAtestRun

length of your name is 14

----------------------------|
[][][][][][][][][][][][][][]|
----------------------------|


I've tried to implement the latter part of the question (i.e. Displaying the histogram in a vertical manner) and have managed to get most of it done. In the code below, The maximum number of words accepted are 20 and the maximum word length is 10. Also, apologies for not getting the best graphical representation of a typical histogram but the logic to display vertical bars is completely accurate! Here's my code,

#define MAXWORDS 20
#define MAXLENGTH 10

int c, nlength = 0, i, nword = 0, j;
    int length_words[20]= {0};

    while((c = getchar()) != EOF && nword <= MAXWORDS)
    {
        if(c != ' ' && c != '\t' && c != '\n')
            ++nlength;
        else
        {
            if(nlength != 0){
                length_words[nword] = nlength;
                ++nword;
               /* for(i = 0; i < nlength; i++)
                printf("O");
                printf("\n");*/
                printf("Word number: %d has length: %d\n", nword - 1, nlength);
            }
            nlength = 0;
        }
    }
    // Displaying the Histogram
    for(i = MAXLENGTH; i > 0; i--)
    {
        for(j = 0; j < nword; j++)
        {
            if(i > length_words[j])
                printf("   ");
            else
                printf(" O ");
        }
        printf("\n");
    }

Feel free to run this and let me know in case of any discrepancy or loopholes!


#include <stdio.h>

int main(void)
{
int i, ii, state, c, largest, highest;
int A[100];
for(i = 0; i < 100; ++i)
    A[i] = 0;
i = ii = state = c = largest = 0;

while((c = getchar()) != EOF)
{
    if(c == ' ' || c == '\b' || c == '\n')
    {
        if(state)
        {
            ++A[i];
            if(largest <= i)
                largest = i;
            i = state = 0;
        }
    }
    else
    {
        if(state)
            ++i;
        else
            state = 1;
    }
}
for(i = 0; i < 100; ++i)
    if(highest <= A[i])
        highest = A[i];

for(i = 0; i <= highest; ++i)
{
    for(ii = 0; ii < 100; ++ii)
        if(A[ii])
        {
            if(A[ii] > (highest - i))
                printf("*\t");
            else
                printf(" \t");
        }
    putchar('\n');
}
for(ii = 0; ii < 100; ++ii)
    if(A[ii])
        printf("-\t");
putchar('\n');

for(ii = 0; ii < 100; ++ii)
    if(A[ii])
        printf("%d\t", ii + 1);
putchar('\n');
return 0;
}


Print Histogram based on word Length. (C Program).

HORIZONTAL VERSION :

#include<stdio.h>
#define MAX 15

int main()
{
    int c, wordLength, count;
    int arr[MAX] = { 0 };
    
    wordLength = count = 0;
    
    while((c = getchar()) != EOF)
    {
        if(c != ' ' && c != '\t' && c != '\n')
            ++arr[count];
        else
            ++count;
    }
    
    for(int i = 0; i <= count; i++)
    {
        printf("\n%2d |", i+1);
        
        for(int k = arr[i]; k > 0; k--)
            printf("— ");
    }
    
    return 0;
}

Input/Output :

man in black thor jsjsk ksskka
 1 |— — —
 2 |— —
 3 |— — — — —
 4 |— — — —
 5 |— — — — —

VERTICAL VERSION :

#include<stdio.h>
#define MAX 15

int main()
{
    int c, count, maxLength;
    int arr[MAX] = { 0 };
    
    maxLength = count = 0;
    
    while((c = getchar()) != EOF)
    {
        if(c != ' ' && c != '\t' && c != '\n')
            ++arr[count];
        else
            ++count;
        
        if(arr[count] > maxLength)
            maxLength = arr[count];
    }
    
    for(int i = 1; i <= maxLength + 2; i++)
    {
        printf("\n");
        for(int k = 0; k <= count; k++)
        {
            if(i <= maxLength)
            {
                if(maxLength - i < arr[k])
                    printf("|");
                else
                    printf(" ");
            }
            else
            {
                if(maxLength - i == -1)
                    printf("—");
                else
                    printf("%d", k+1);
            }
            
            printf(" ");
        }
        
    }
    
    return 0;
}

Input/Output :

jsjs sjsj sjsj sjskks sjs sjs
      |
      |
| | | |
| | | | | |
| | | | | |
| | | | | |
— — — — — —
1 2 3 4 5 6


here is the method that I used :

#include <stdio.h>

#define MAXWD 25 // maximum words 
#define MAXLW 20 // maximum lenght of word or characters

int     main(void) {
    int word[MAXWD]; // an array consists of 25 words 
    int c, i, j, nc, nw; // declaring c for the input, i for the loop 'for', j is for printing left to right, nc and nw stands for new character and new word

    for (i = 0; i < 25; ++i)
            word[i] = 0;

    nc = nw = 0; // set to count from 0

    while ((c = getchar()) != EOF) {

            ++nc;
            if ( c == ' ' || c == '\t' || c == '\n') {

                    word[nw] = nc -1; // for deleting the space to go for the new word

                    ++nw;
                    nc = 0; // start counting from zero
            }
    }
    for (i = MAXLW; i >= 1; --i) {
            for(j = 0; j <= nw; ++j) { // to start from left to wright
                    if (i <= word[j]) // MAXLW is less  or equal to the number of words
                            putchar('|');
                    else
                            putchar(' ');
            }
            putchar ('\n');
    }
    return 0;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜