[K&R 1-13, according to Coos Haak]:
Write a program to print a histogram of thel engths of words
ini tsinput. It is easy to draw the histogram with the bars horizontal;
a vertical orientation is more challenging.
Thank you.
(I'm attempting the vertical orientation....)
Okay. Let us recapitulate _why_ a vertical orientation is more
challenging: because you can only rely on line-by-line output
- Horizontal: Print the length the bar is for, then print the
number of * belonging to the bar
- Vertical: You need to output each bar column with sufficient
distance that the column headings do not run into each other.
In addition, you need to output * for all bars which reach down
to the current line (denoting the current number of *).
Example: 4 letters 6 times, 5 letters 3 times.
line 0: 2 3 4 5 6
line 1: * * || 2, 3, 6 appear 0 times, 0 < line number
line 2: * *
line 3: * *
line 4: * || 5 appears 3 times, 3 < line number
line 5: *
line 6: *
line 7: || 4 appears 6 times, 6 < line number
Consider moving the output to a function of its own -- then you
can change the output function or switch output functions as
you like.
#include <stdio.h>
int main(void)
{
int c, nl, w;
c = nl = w = 0;
int noOfWords[10];
Make this
int noOfWords[10] = {0};
-- otherwise, you access noOfWords[0] without previous
definition (words of length zero are seldom).
/* ctrl c doesn't work properly $ used */
while((c = getchar()) != '$') {
++nl;
Think hard about that part: Do you really want to increase
nl unconditionally?
if (c == ' ' || c == '\t' || c == '\n') {
Alternative: if (isspace(c))
/* hold no of characters for each word.. */
noOfWords[w] = nl;
++w;
Umh, the other way round: You want to count the words
with a certain number of letters, i.e.
noOfWords[nl]++;
If you gave the variables slightly more speaking names, I could
guess what you are trying to do with "w".
Note that you only can access noOfWords[0] through noOfWords[9],
i.e. more than nine letters per word lead to problems.
As there cannot be any words of length 0, I would store the
amount of words with more than nine letters in noOfWords[0].
/* reset the letter count */
nl = 0;
}
}
int i, j;
One more C99ism: Standard C90 does not allow declarations after
other statements.
That is
i < 10
or, better,
i < numCol
where numCol = sizeof noOfWords / sizeof noOfWords[0]
for (j = 0; j < noOfWords; ++j) {
printf("*\n"); /* STUCK */
}
printf("\n");
}
You output for all columns you wrote to the number
of * stored there.
What you need is "For every column: As long as the number
of asterisks output (i.e. the number of written lines) is
less than the number of words with that letter, output
another line with that asteriskSet":
int asteriskFound = 1;
int line = 1;
....
while (asteriskFound) {
asteriskFound = 0;
putchar(' '); /* distance between the columns */
for (numLetters = 1; numLetters < numCol; ++numLetters) {
if (noOfWords[numLetters] >= line) {
asteriskFound = 1;
putchar('*');
}
else {
putchar(' ');
}
}
putchar('\n');
++line;
}
<snip>
I suggest another way of writing the programme; if you do not want
to "spoil" the exercise, read on after the end:
,---
#include <stdio.h>
#include <ctype.h> /* for isspace() */
void countLetters(int *numWords, int numCol, int stopAt);
void printStat(const int *numWords, int numCol);
int main (void)
{
int noOfWords[10] = {0};
int len = sizeof noOfWords / sizeof noOfWords[0];
countLetters(noOfWords, len, '$');
printStat(noOfWords, len);
return 0;
}
void countLetters(int* numWords, int numCol, int stopAt)
{
int c, numLetters;
c = numLetters = 0;
/* ctrl c doesn't work properly $ used */
while((c = getchar()) != stopAt) {
++numLetters;
if (isspace(c)) {
/* hold no of characters for each word.. */
if (numLetters < numCol) {
++numWords[numLetters];
}
else {
/* store the ones that don't fit somewhere */
++numWords[0];
}
/* reset the letter count */
numLetters = 0;
}
}
}
void printStat(const int *numWords, int numCol)
{
int numLetters;
int asteriskFound = 1;
int line = 1;
for (numLetters = 1; numLetters < numCol; ++numLetters) {
printf(" %2d", numLetters);
}
printf(" other\n");
while (asteriskFound) {
asteriskFound = 0;
for (numLetters = 1; numLetters < numCol; ++numLetters) {
printf(" "); /* distance between the columns */
if (numWords[numLetters] >= line) {
asteriskFound = 1;
putchar('*');
}
else {
putchar(' ');
}
}
printf(" "); /* distance between the columns */
if (numWords[0] >= line) {
asteriskFound = 1;
putchar('*');
}
else {
putchar(' ');
}
putchar('\n');
++line;
}
}
`---
Note that countLetters() still does not work correctly:
An input of " $" leads to an output of
1 2 3 4 5 6 7 8 9 other
*
*
*
*
*
*
*
Other functions you may find useful in this context:
isalpha()
or
isalnum()
depending on what you want to see as "word".
If you wonder about the declaration
void countLetters(int *numWords, int numCol, int stopAt);
and especially the numWords part (I do not have my K&R handy,
so I do not know what you should know up to now), then just
think of
int *numWords
as "You can treat me like an array but have to know my size".
I introduced stopAt because I wanted to be able to process
files via
KR1_13 <myFile
-- for this I needed EOF instead of '$'.
Cheers
Michael