K&R histogram help

C

c19h28o2

Hi,

Guy's I know there are several posts about this, however I do not want
to read them as answers are undoubtedly posted!

Here is my attempt but I'm slightly stuck. I'm not looking for the
answer I'm looking for a point in the right direction.....

#include <stdio.h>

int main(void)
{
int c, nl, w;
c = nl = w = 0;
int noOfWords[10];
//ctrl c doesn't work properly $ used
while((c = getchar()) != '$') {
++nl;
if (c == ' ' || c == '\t' || c == '\n') {
//hold no of characters for each word...
noOfWords[w] = nl;
++w;
//reset the letter count
nl = 0;
}
}

int i, j;

for (i = 0; i < w; ++i){

for (j = 0; j < noOfWords; ++j) {
if( //tbc ){ //I'm stuck here I'm I'm

//trying to say where am I? is it
printf("*"); //time to print a asterisk or a
space?????
}else {
printf(" ");
}

}
printf("\n");
}

return 0;
}

Am I even going in the right direction??????

Any help would be greatly appreciated....

Thanks

Michael
 
M

Michael Mair

c19h28o2 said:
Guy's I know there are several posts about this, however I do not want
to read them as answers are undoubtedly posted!

Here is my attempt but I'm slightly stuck. I'm not looking for the
answer I'm looking for a point in the right direction.....

Give us a fighting chance: Which K&R exercise?
For those who do not have their K&R handy: Short outline.

More important: What did you expect? What does happen?
#include <stdio.h>

int main(void)
{
int c, nl, w;
c = nl = w = 0;
int noOfWords[10];
//ctrl c doesn't work properly $ used

Note: // comments are not a part of C90 (they came in with C99);
in addition, in usenet they have the disadvantage of giving you
line breaks that may change compilability or semantics of your
programme.
while((c = getchar()) != '$') {

What will you do on c == EOF?
++nl;
if (c == ' ' || c == '\t' || c == '\n') {
//hold no of characters for each word...
noOfWords[w] = nl;
++w;

You need to break out of the loop if w >= 10.
//reset the letter count
nl = 0;
}
}

int i, j;

for (i = 0; i < w; ++i){

for (j = 0; j < noOfWords; ++j) {
if( //tbc ){ //I'm stuck here I'm I'm

//trying to say where am I? is it
printf("*"); //time to print a asterisk or a
space?????


As I do not have the least clue what you are trying to achieve,
I cannot tell you that.
BTW: The above illustrates nicely what I said about line breaks
and // comments. The tabs do not help, either.
}else {
printf(" ");
}

}
printf("\n");
}

return 0;
}

Am I even going in the right direction??????

Any help would be greatly appreciated....

Help us help you by asking your question in a better way.


Cheers
Michael
 
C

CBFalconer

c19h28o2 said:
Guy's I know there are several posts about this, however I do not
want to read them as answers are undoubtedly posted!

Here is my attempt but I'm slightly stuck. I'm not looking for the
answer I'm looking for a point in the right direction.....
.... snip ...

for (i = 0; i < w; ++i){

for (j = 0; j < noOfWords; ++j) {
if( //tbc ){ //I'm stuck here I'm I'm


Don't use tabs, use spaces. Do limit line lengths to about 65
chars for usenet. Don't use // comments in usenet, they don't
survive line wrapping. Besides which they are illegal in C90.

If you want people to try your code they have to be able to cut and
paste it into their editors, without having to fix all those
nuisances.

--
Some informative links:
http://www.geocities.com/nnqweb/
http://www.catb.org/~esr/faqs/smart-questions.html
http://www.caliburn.nl/topposting.html
http://www.netmeister.org/news/learn2quote.html
 
C

c19h28o2

Right,

let me try again....

Michael said:
Give us a fighting chance: Which K&R exercise?
For those who do not have their K&R handy: Short outline.

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.

(I'm attempting the vertical orientation....)
Note: // comments are not a part of C90 (they came in with C99);
in addition, in usenet they have the disadvantage of giving you
line breaks that may change compilability or semantics of your
programme.

Amended......


#include <stdio.h>

int main(void)
{
int c, nl, w;
c = nl = w = 0;
int noOfWords[10];
/* ctrl c doesn't work properly $ used */
while((c = getchar()) != '$') {
++nl;
if (c == ' ' || c == '\t' || c == '\n') {
/* hold no of characters for each word.. */
noOfWords[w] = nl;
++w;
/* reset the letter count */
nl = 0;
}
}

int i, j;

for (i = 0; i < w; ++i){

for (j = 0; j < noOfWords; ++j) {
printf("*\n"); /* STUCK */
}
printf("\n");
}

return 0;
}

The program prints the lines correctly but one below the other i.e.

*
*
*

*
*
*
*
*

I'm slightly stuck on how to print them side by side........
 
C

Coos Haak

Op 23 Sep 2006 14:16:55 -0700 schreef c19h28o2:
Right,

let me try again....



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;

You could have said example 1-13.
Does your keyboard have a problem with spaces?
... the lenghts ..
... in its input ..
 
C

c19h28o2

Coos said:
Op 23 Sep 2006 14:16:55 -0700 schreef c19h28o2:


You could have said example 1-13.
Does your keyboard have a problem with spaces?
.. the lenghts ..
.. in its input ..

Hmmmm it appears people are more interested in the format of my
usenet post then actually helping me....

No my computer does not have a problem with spaces, if you care to read
above
I was told not to use tabs......

For any further readers of this post if you have nothing constructive
to say
please DO NOT SAY IT.

Thanks

Michael
 
M

Michael Mair

[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.
for (i = 0; i < w; ++i){

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;
}
return 0;
}
<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
 
C

c19h28o2

Michael said:
[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.
for (i = 0; i < w; ++i){

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;
}
return 0;
}
<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


Michael,

Thank you very much, the problem with this was my way of thinking! I
was trying to print the number of stars per letter rather than the way
you solved the problem via the number of words per star!!!! Note to
self: must learn to interpret questions correctly!

Ahhhh so int* numWords would point to the noOfWords array? pass by ref?


Also on the line "if (isspace(c)) { ....." does isspace counts
newlines, tabs etc?
 
M

Michael Mair

c19h28o2 said:
Michael said:
c19h28o2 schrieb:
[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.
(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 *).
Thank you very much, the problem with this was my way of thinking! I
was trying to print the number of stars per letter rather than the way
you solved the problem via the number of words per star!!!! Note to
self: must learn to interpret questions correctly!

This happens often enough -- with time you learn to look at it two
and more times... :)

Ahhhh so int* numWords would point to the noOfWords array? pass by ref?

Yes. As I said, I do not know whether you have "learnt" that yet
but IMO the programme benefits from separating counting and printing.

Note that there is "no such thing as pass by reference in C".
A pointer is a variable holding an address; you can modify the
object at that address through a pointer passed to a function
as long as the pointer is a valid address for an object of this
kind. You cannot modify the address itself -- for this you'd need
a pointer to a pointer.
Pointers and arrays are closely related. Most of the time, when
you see an array identifier, it means the same as the address of
its first element. This is also what you are passing: For
Type array[SIZE], singleObject;
foo(array, SIZE);
foo(&singleObject, 1);
bar(&singleObject);
....
void foo (Type *pType, int numElems)
{
....
}
void bar (Type *pType)
{
....
}
the first function call could be equivalently replaced by
foo(&array[0], SIZE);

This close relationship blurs the picture for beginners.

If you want to know more, I recommend having a look at the
chapter about arrays and pointers in the c.l.c FAQ at
http://c-faq.com/

Also on the line "if (isspace(c)) { ....." does isspace counts
newlines, tabs etc?

K&R has a reference of the standard library function; your
environment or IDE usually also has documentation of the standard
library; there you can find out what this function does exactly
for _your_ implementation. The C standard says the following:
,-- 7.4.1.10 The isspace function --
Synopsis
1
#include <ctype.h>
int isspace(int c);

Description
2 The isspace function tests for any character that is a standard
white-space character or is one of a locale-specific set of
characters for which isalnum is false. The standard white-space
characters are the following: space (' '), form feed ('\f'),
new-line ('\n'), carriage return ('\r'), horizontal tab ('\t'),
and vertical tab ('\v'). In the "C" locale, isspace returns true
only for the standard white-space characters.
`----
If this is not what you need, then I'd still recommend a function
allowing you to test "isWordChar()" or "isWhitespace()" which
holds your interpretation of "word". This function may come in
handy for further exercises.

Cheers
Michael
 
C

c19h28o2

Note that there is "no such thing as pass by reference in C".
A pointer is a variable holding an address; you can modify the
object at that address through a pointer passed to a function
as long as the pointer is a valid address for an object of this
kind. You cannot modify the address itself -- for this you'd need
a pointer to a pointer.

Michael,

Thanks for clearing that up, here is my explaination of it (correct me
if i'm wrong on this explanation....) I've done a bit of research on
the topic and on the surface it appears that the value is passed by
reference however this is not the case but is actually simulted by c
i.e.

int main()
{
int a;
int b;
a=5;
b = 7;
foo(&a, &b);
printf("%d %d\n", a,b);
}

void foo(int *x, int *y)
{
*x = 6;
*y = 8;
}

Although the addresses of a and b via &a, &b are passed to the foo
function, the actual function doesn't use the addresses to modify the
value it uses the dereference operator *x, *y which is indirectly
changing the value hence the simulated pass by reference.....

Would you agree with that Michael?

Cheers

Michael
 
C

c19h28o2

value it uses the dereference operator *x, *y which is indirectly

As a note to the above line, the dereference operator points to the
value hence it is still pass by value....

Thanks

Michael
 
M

Michael Mair

c19h28o2 said:
Thanks for clearing that up, here is my explaination of it (correct me
if i'm wrong on this explanation....) I've done a bit of research on
the topic and on the surface it appears that the value is passed by
reference however this is not the case but is actually simulted by c
i.e.

int main()
{
int a;
int b;
a=5;
b = 7;
foo(&a, &b);
printf("%d %d\n", a,b);
}

void foo(int *x, int *y)
{
*x = 6;
*y = 8;
}

Although the addresses of a and b via &a, &b are passed to the foo
function, the actual function doesn't use the addresses to modify the
value it uses the dereference operator *x, *y which is indirectly
changing the value hence the simulated pass by reference.....

Would you agree with that Michael?

Sorry for taking so long to respond -- I originally pushed the
message back to "think about it later" and forgot it afterwards...

You probably mean the right thing; however:
The function _does_ use the addresses to modify the objects stored
at the respective address. What foo() cannot do is modify the
passed addresses in a way that the caller, main(), is affected.
E.g.
#include <stdio.h>

void foo (int *pA, int *pB);
void bar (int *pA, int *pB);
void baz (int **ppA, int **ppB);

int main (void)
{
int a, b;
int *pA, *pB;
a=5;
b = 7;
pA = &a;
pB = &b;
foo(pA, pB);
printf("%d %d\n", a,b);
bar(pA, pB);
printf("%d %d\n", *pA, *pB);
baz(&pA, &pB);
printf("%d %d\n", *pA, *pB);
return 0;
}

void foo(int *x, int *y)
{
*x = 6;
*y = 8;
}

void bar (int *pA, int *pB)
{
int *temp = pA;
pA = pB;
pB = temp;
}

void baz (int **ppA, int **ppB)
{
int *temp = *ppA;
*ppA = *ppB;
*ppB = temp;
}

An excellent point to continue after you managed the comp.lang.c
FAQ is Chris Torek's "C for Smarties":
http://web.torek.net/torek/c/index.html
This page and other pages can be found via the comp.lang.c wiki:
http://clc-wiki.net


Cheers
Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top