Ré-apprentissage du C : Exercices du K&R :)

Le
Eddahbi Karim
Salut,

Ça fait longtemps que j'ai plus posté ici, et pour cause j'ai arrété le
C jusqu'à avoir un meilleur bouquin que le dernier que j'avais.
J'ai donc acheté le K&R (The C Programming Language : ANSI C) et je
fais donc les exercices du tutorial pour me remettre d'applomb.

L'exercice que j'avais à faire est donc le suivant :

Exercise 1-13. Write a program to print a histogram of the lengths of
words. It is easy to draw the histogram with the bars horizontal; a
vertical orientation is more challenging.

Voici donc ce que j'ai fait :

/*-8<--

WLF-VH.c -- is an acronym for Word Lengths Frequencies - Vertical
Histogram. Like the acronym (badly) said, this program
will display a vertical histogram representing the
number of occurences found for each length of word
starting from 1 char to 30 chars.

Author : Eddahbi Karim
E-mail : installation_fault_association@yahoo.fr

-

Usage : Launch the program and put some words in.
You can replace the input by a file, just do :
program < File

Note : - This program is the result of an exercice of the K&R
(1.13, The C Programming Language : Second Edition)
I can't use pointers, functions, structures
- This program is, unfortunately, limited to 30 chars by
word so the histogram can fit on a lot of terminals.
- The maximum is limited to 50 occurences of each length
because it's troublesome to display the histogram after
that limit.
- The use of int can lead to overflows but that's just a
exercice of the tutorial
- An array begin at 0, so the last cell of length is :
length[49]. To store a length of 50 chars, we must
do : length[nchar-1];

-->8-*/

#include <stdio.h>

/* In and Outside a word */
#define IN 1
#define OUT 0

/* The maximum number of chars allowed in a word */
#define SIZE_LIMIT 30

/* The maximal number of occurences for each length */
#define FREQ_LIMIT 50

int main ( void )
{

/* The input storage variable : Store 1 char at time */
int c;

/* The input state : Check whether we are in a word or not */
int state;

/* The number of chars */
long int nchar;

/* A counter */
int i;

/* The highest frequency (number of occurences) */
long int max;

/* Words lengths */
long int lengths[SIZE_LIMIT];

for (i = 0; i < SIZE_LIMIT; i++)
lengths[i] = 0;

max = 0;
nchar = 0;
state = OUT;
c = 0;

/* We read the whole input to store the lengths of words */
while ((c = getchar()) != EOF)
{
/* If we find a char*/
if (c != ' ' && c != '' && c != 't')
{
/* And we were outside a word, we toggle "state" and
we begin to measure the length of word */
if (state == OUT)
{
state = IN;
++nchar;
}

/* Else we continue to measure */
else
++nchar;
}

/* If we come outside a word, store the length of the word
where we came from and reset 'nchar'. (See Note 4) */
else if (state == IN)
{
state = OUT;

/* Verify the word length */
if (nchar <= SIZE_LIMIT)
++lengths[nchar-1];
else if (nchar > SIZE_LIMIT)
printf("Word too big (more than 50 chars),"
" skipping this one.");
else if (nchar < 0)
printf("We got an overflow word to big, skipping "
"this one.");
nchar = 0;
}
}

/* Now get the 'max'imal frequency in lengths[50] */
for (i = 0; i < SIZE_LIMIT; i++)
{

/* Try to detect overflows before affecting max */
if (lengths[i] < 0)
{
printf("An overflow has been detected for the words of %d "
"chars. Re-calibrating the frequency to %d.",
i, FREQ_LIMIT);
lengths[i] = 50;
}

if (max < lengths[i])
max = lengths[i];
}

/* Restrict "max" to FREQ_LIMIT */
if (max > FREQ_LIMIT)
{
printf("The real maximum is %ld, but we must restrict the "
"size of the histogram.", max);
max = FREQ_LIMIT;
}
else if ( max < 0 )
{
printf("We got an overflow, we will put max to %d",
FREQ_LIMIT);
max = FREQ_LIMIT;
}

/* The last phase. Description :
- max is :
- The current frequency displayed
- The current line number
- At line 48, to display a '|' for words found more than 48
times, we must check if the frequency of this length is superior
or equal to "max".
*/

for (; max > 0; max--)
{
/* Print the current frequency displayed on this line */
printf("%3ld", max);

/* See "The Last phase" */
for (i = 0; i < SIZE_LIMIT; i++)
if (lengths[i] >= max)
printf(" |");
else
printf(" ");

/* One frequency per line */
putchar('');
}

/* The foot of the histogram */
printf("f/n");

for (i = 1; i <= SIZE_LIMIT; i++)
printf("%3d", i);

putchar('');

return 0;

}

Voilà, si vous avez des suggestions, des remarques constructives :D.
Je rappelle que c'est un exercice du chapitre 1 d'introduction, je suis
donc limité dans les fonctions :).

Voilà,

--
--
ThE_TemPLaR
  • Partager ce contenu :
Vos réponses
Trier par : date / pertinence
Tobias Oed
Le #579331
Eddahbi Karim wrote:

Salut,

Ça fait longtemps que j'ai plus posté ici,


Moi non plus!

et pour cause j'ai arrété le
C jusqu'à avoir un meilleur bouquin que le dernier que j'avais.


Moi c'est a cause de stroustroup.

J'ai donc acheté le K&R (The C Programming Language : ANSI C) et je
fais donc les exercices du tutorial pour me remettre d'applomb.

L'exercice que j'avais à faire est donc le suivant :

Exercise 1-13. Write a program to print a histogram of the lengths of
words. It is easy to draw the histogram with the bars horizontal; a
vertical orientation is more challenging.

Voici donc ce que j'ai fait :

/*-8<-----------------------------------------------------------------

WLF-VH.c -- is an acronym for Word Lengths Frequencies - Vertical
Histogram. Like the acronym (badly) said, this program
will display a vertical histogram representing the
number of occurences found for each length of word
starting from 1 char to 30 chars.

Author : Eddahbi Karim
E-mail :

----------------------------------------------------------------------

Usage : Launch the program and put some words in.
You can replace the input by a file, just do :
program < File

Note : - This program is the result of an exercice of the K&R
(1.13, The C Programming Language : Second Edition)
I can't use pointers, functions, structures...
- This program is, unfortunately, limited to 30 chars by
word so the histogram can fit on a lot of terminals.
- The maximum is limited to 50 occurences of each length
because it's troublesome to display the histogram after
that limit.
- The use of int can lead to overflows but that's just a
exercice of the tutorial...
- An array begin at 0, so the last cell of length is :
length[49]. To store a length of 50 chars, we must
do : length[nchar-1];

----------------------------------------------------------------->8-*/

#include
/* In and Outside a word */
#define IN 1
#define OUT 0

/* The maximum number of chars allowed in a word */
#define SIZE_LIMIT 30

/* The maximal number of occurences for each length */
#define FREQ_LIMIT 50

int main ( void )
{

/* The input storage variable : Store 1 char at time */
int c;

/* The input state : Check whether we are in a word or not */
int state;

/* The number of chars */
long int nchar;

/* A counter */
int i;

/* The highest frequency (number of occurences) */
long int max;

/* Words lengths */
long int lengths[SIZE_LIMIT];


Tres mauvais choix du nom de variable. histogram ou n_occurences

for (i = 0; i < SIZE_LIMIT; i++)
lengths[i] = 0;

max = 0;
nchar = 0;
state = OUT;
c = 0;

/* We read the whole input to store the lengths of words */
while ((c = getchar()) != EOF)
{
/* If we find a char...*/
if (c != ' ' && c != 'n' && c != 't')
{
/* And we were outside a word, we toggle "state" and
we begin to measure the length of word */
if (state == OUT)
{
state = IN;
++nchar;
}

/* Else we continue to measure */
else
++nchar;


C'est bien complique. Je remplacerai tout le if avec
STATE=IN;
++nchar; [* voir plus bas]

}

/* If we come outside a word, store the length of the word
where we came from and reset 'nchar'. (See Note 4) */
else if (state == IN)
{
state = OUT;

/* Verify the word length */
if (nchar <= SIZE_LIMIT)
++lengths[nchar-1];


[**]

else if (nchar > SIZE_LIMIT)
printf("Word too big (more than 50 chars),"


Tu t'embrouille tout seul entre tes 2 limites. SIZE_MAX c'est 30. Utilise un %d pour
imprimer cette valeur (c'est tout l'interet de la macro: le nombre magique est a un
seul endroit).

" skipping this one.n");
else if (nchar < 0)
printf("We got an overflow... word to big, skipping "
"this one.n");


Ca arrive quand ca? Quand un mot est plus grand que LONG_MAX? Peu probabale. De plus
l'overfolow sur les entier signes n'est pas garanti (il me semble). Meme si c'etait
garanti que fait ton code si nchar redevient positif?
Pour palier a ca, il vaudrait mieux remplacer le ++n_char; [*] par un
if (n_char <= SIZE_LIMIT){
++n_char;
}

nchar = 0;
}
}

/* Now get the 'max'imal frequency in lengths[50] */
for (i = 0; i < SIZE_LIMIT; i++)
{

/* Try to detect overflows before affecting max */
if (lengths[i] < 0)
{
printf("An overflow has been detected for the words of %d "
"chars. Re-calibrating the frequency to %d.n",
i, FREQ_LIMIT);


c'est pour detecter le cas ou il y plus de LONG_MAX occurences de mots d'une
certaine longueur? meme probleme que plus haut. Refait [**] pour eviter l'overflow
des le depart.

lengths[i] = 50;
}

if (max < lengths[i])
max = lengths[i];
}

/* Restrict "max" to FREQ_LIMIT */
if (max > FREQ_LIMIT)
{
printf("The real maximum is %ld, but we must restrict the "
"size of the histogram.n", max);
max = FREQ_LIMIT;
}
else if ( max < 0 )
{


condition impossible.

printf("We got an overflow, we will put max to %d...n",
FREQ_LIMIT);
max = FREQ_LIMIT;
}

/* The last phase. Description :
- max is :
- The current frequency displayed
- The current line number
- At line 48, to display a '|' for words found more than 48
times, we must check if the frequency of this length is superior
or equal to "max".
*/


comprends rien au commentaire. D'ou il sort le 48?


for (; max > 0; max--)
{
/* Print the current frequency displayed on this line */
printf("%3ld", max);

/* See "The Last phase" */
for (i = 0; i < SIZE_LIMIT; i++)
if (lengths[i] >= max)
printf(" |");
else
printf(" ");

/* One frequency per line */
putchar('n');
}

/* The foot of the histogram */
printf("f/n");

for (i = 1; i <= SIZE_LIMIT; i++)
printf("%3d", i);

putchar('n');

return 0;

}

Voilà, si vous avez des suggestions, des remarques constructives :D.


C'est tres louable d'essayer de gerer les overflow mais s'il y a risque d'overflows
il est souvent plus simple de l'eviter au depart plustot que d'essayer de le
detecter plus tard.

Je rappelle que c'est un exercice du chapitre 1 d'introduction, je suis
donc limité dans les fonctions :).


N'empeche tu pourrait ecrire tes propres fonctions.
Comme celle ci pour trouver le max de longeur:

int array_max(int x[],int n){
int i;
int max = x[0];
for(i = 0; i < n; i++){
if (x[i] > max){
max = x;
}
}
return max;
}

A oui, j'oubliais: Met des accolades.

a+ Tob.

Antoine Leca
Le #585932
[Remarque: je n'ai pas compilé ton programme]

En ,
Eddahbi Karim va escriure:
/* The number of chars */
long int nchar;


Pourquoi des long ?

/* A counter */
int i;


L'intérêt du commentaire ? Bon, je pinaille.


max = 0;


Déplace cette initialisation juste avant la boucle où elle doit servir (ou
mieux, sépare cette fonction en deux...)

/* If we find a char...*/
if (c != ' ' && c != 'n' && c != 't')


isspace()m, ce qui te donnera l'idée d'utiliser ! isalpha()...


if (nchar <= SIZE_LIMIT)
++lengths[nchar-1];


L'intérêt de soustraire 1 ? Chercher à tromper celui qui viendrait après
maintenir le programme ?

else if (nchar > SIZE_LIMIT)
printf("Word too big (more than 50 chars),"
" skipping this one.n");


Et le jour où tu changes la constante, le message d'erreur devient
obsolète... Si tu vois ce que je veux dire ;-)

else if (nchar < 0)
printf("We got an overflow... word to big, skipping "
"this one.n");


Le test est plutôt rigolo (avec nchar long), mais en plus on peut le prendre
en défaut, si on fait la boucle et si on revient dans les négatifs...

/* Try to detect overflows before affecting max */
if (lengths[i] < 0)


Je me demande comment cela peut arriver (avec des longs), mais bon, c'est
bien de contrôler cela. Même remarque que ci-dessus, le débordement peut
exister alors même que le compteur est positif, si « on fait le tour ».

else if ( max < 0 )


Quel cas de figure peut produire cela ?

for (; max > 0; max--)


On est dans la boucle des lignes...

/* Print the current frequency displayed on this line */
printf("%3ld", max);


Ici on imprime l'étiquette de la fréquence actuelle, bien

for (i = 0; i < SIZE_LIMIT; i++)


On boucle (sur la même ligne) sur les fréquences

printf(" |");


... puis on imprime l'histogramme, chaque case sur 3 caractères, bien.

for (i = 1; i <= SIZE_LIMIT; i++)
printf("%3d", i);


Maintenant, la boucle des longueurs de mots. Chjaque longueur sur 3
caractères. Mais on a pas laissé au début l'équivalent pour l'étiquette,
donc tout est décalé de 1: la première valeur (1) s'affiche sous la colonne
des étiquettes, le 2 sous le premier histogramme, etc. et sous le dernier
histogramme il n'y a rien.

Remarque que tu si « corriges » en écrivant
for (i = 0; i <= SIZE_LIMIT; i++)
tu vas avoir un résultat apparement correct, mais c'est pire, parce que tu
joues avec le fait que tu stockes la fréquence des mots de longueur L dans
length[L-1]: résultat, celui qui essayes de corriger quelque chose est
admissible d'office à l'infirmerie pour mal de crâne carabiné...

Faire un for(...0; ...<...) puis un for(...1; ...<=...), c'est vraiment
cherché à rendre le programme incompréhensible.



En espérant que cela aide.

Antoine

Eddahbi Karim
Le #585931
On Mon, 23 Feb 2004 15:04:19 +0100
"Antoine Leca"
Note : Bon, j'ai un problème de gestion avec certains charsets sur mon
lecteur de news.

[Remarque: je n'ai pas compil_ ton programme]

/* The number of chars */
long int nchar;


Pourquoi des long ?


Ça permet de limiter les problèmes avec les overflows. Je pourrais
utiliser un unsigned, mais on est pas censé l'avoir appris à ce niveau
là.

J'essais de faire les problèmes avec les outils qu'on donne.

max = 0;


D_place cette initialisation juste avant la boucle o_ elle doit servir
(ou mieux, s_pare cette fonction en deux...)

/* If we find a char...*/
if (c != ' ' && c != 'n' && c != 't')


isspace()m, ce qui te donnera l'id_e d'utiliser ! isalpha()...


T'es pas censé connaitre isspace et isalpha à ce moment là :).
C'est bien ctype.h pour ce type de gestion mais je le fais avec les
outils que l'on donne ;).



if (nchar <= SIZE_LIMIT)
++lengths[nchar-1];


L'int_r_t de soustraire 1 ? Chercher _ tromper celui qui viendrait
apr_s maintenir le programme ?


Si le mot fait 50 caractères et je stocke dans lengths[50], je vais
faire un overflow...
Le -1 vient du fait que on compte à partir de 0 dans les arrays.


else if (nchar > SIZE_LIMIT)
printf("Word too big (more than 50 chars),"
" skipping this one.n");


Et le jour o_ tu changes la constante, le message d'erreur devient
obsol_te... Si tu vois ce que je veux dire ;-)


Bien vu !
Je corrige...


else if (nchar < 0)
printf("We got an overflow... word to big, skipping "
"this one.n");


Le test est plut_t rigolo (avec nchar long), mais en plus on peut le
prendre en d_faut, si on fait la boucle et si on revient dans les
n_gatifs...


Il peut faire un tour complet effectivement, je vais tenter de limiter
le nombre à la boucle car je peux pas prévenir d'un tour complet.


/* Try to detect overflows before affecting max */
if (lengths[i] < 0)


Je me demande comment cela peut arriver (avec des longs), mais bon,
c'est bien de contr_ler cela. M_me remarque que ci-dessus, le
d_bordement peut exister alors m_me que le compteur est positif, si _
on fait le tour _.


Effectivement...


else if ( max < 0 )


Quel cas de figure peut produire cela ?


Aucun, c'est un test que j'avais mis au début et que j'aurais du
enlevé...


for (; max > 0; max--)


On est dans la boucle des lignes...


Tu veux que je renomme max en line ?

[snip]

Maintenant, la boucle des longueurs de mots. Chjaque longueur sur 3
caract_res. Mais on a pas laiss_ au d_but l'_quivalent pour
l'_tiquette, donc tout est d_cal_ de 1: la premi_re valeur (1)
s'affiche sous la colonne des _tiquettes, le 2 sous le premier
histogramme, etc. et sous le dernier histogramme il n'y a rien.


Non non non, y'a eu un printf("f/n") Juste au dessus qui fait 3
caractères.
J'avoue que ça parait trompeur mais j'ai pas fait exprès de l'appeler
'n' :).
C'est pas un printf("fn") !
Donc ça fait :

ETQ |
f/n 1


Remarque que tu si _ corriges _ en _crivant
for (i = 0; i <= SIZE_LIMIT; i++)
tu vas avoir un r_sultat apparement correct, mais c'est pire, parce
que tu joues avec le fait que tu stockes la fr_quence des mots de
longueur L dans length[L-1]: r_sultat, celui qui essayes de corriger
quelque chose est admissible d'office _ l'infirmerie pour mal de cr_ne
carabin_...


Pourquoi démarrer à 0 ? On affiche les longueurs affichées auparavant.
Or, on stocke pas les mots ayant 0 caractères car un mot de 0 caractères
n'est pas un mot :)


Faire un for(...0; ...<...) puis un for(...1; ...<=...), c'est
vraiment cherch_ _ rendre le programme incompr_hensible.




On calcule d'abord sur des arrays puis sur un compteur aussi...


En esp_rant que cela aide.



Je vais quand même tenter de revoir la chose :)

Antoine




Karim


--
--
ThE_TemPLaR


Eddahbi Karim
Le #585929
Voici une version modifiée :

/*-8<-----------------------------------------------------------------

WLF-VH.c -- is an acronym for Word Lengths Frequencies - Vertical
Histogram. Like the acronym (badly) said, this program
will display a vertical histogram representing the
number of occurences found for each length of word
starting from 1 char to 30 chars.

Author : Eddahbi Karim
E-mail :

----------------------------------------------------------------------

Usage : Launch the program and put some words in.
You can replace the input by a file, just do :
program < File

Note : - This program is the result of an exercice of the K&R
(1.13, The C Programming Language : Second Edition)
I can't use pointers, functions, structures...
- This program is, unfortunately, limited to 30 chars by
word so the histogram can fit on a lot of terminals.
- The maximum is limited to 50 occurences of each length
because it's troublesome to display the histogram after
that limit.
- The use of int can lead to overflows but that's just a
exercice of the tutorial...

----------------------------------------------------------------->8-*/

#include
/* In and Outside a word */
#define IN 1
#define OUT 0

/* The maximum number of chars allowed in a word */
#define SIZE_LIMIT 40

/* The maximal number of occurences for each length */
#define FREQ_LIMIT 50

int main ( void )
{

/* The input storage variable : Store 1 char at time */
int c;

/* The input state : Check whether we are in a word or not */
int state;

/* The number of chars */
long int nchar;

/* A counter */
int i;

/* The highest frequency (number of occurences) */
long int max;

/* Words lengths */
long int lengths[SIZE_LIMIT+1];

for (i = 0; i < (SIZE_LIMIT+1); i++)
lengths[i] = 0;

max = 0;
nchar = 0;
state = OUT;
c = 0;

/* We read the whole input to store the lengths of words */
while ((c = getchar()) != EOF)
{
/* If we find a char...*/
if (c != ' ' && c != 'n' && c != 't')
{
/* And we were outside a word, we toggle "state" and
we begin to measure the length of word */
if (state == OUT)
{
state = IN;
++nchar;

/* If the word is too big, limit it to the
maximum length allowed */
if (nchar > SIZE_LIMIT)
nchar = SIZE_LIMIT+1;
}

/* Else we continue to measure */
else
++nchar;

/* Word too big : reset it to the maximum allowed */
if (nchar > SIZE_LIMIT)
nchar = SIZE_LIMIT+1;
}

/* If we come outside a word, store the length of the word
where we came from and reset 'nchar'. (See Note 4) */
else if (state == IN)
{
state = OUT;

/* If a word is too long, warn the user */
if (nchar == SIZE_LIMIT+1)
printf("Word out of range... (%d chars)n", SIZE_LIMIT);

++lengths[nchar];

/* If a length has been found too many times, limit it
at the maximum of occurences allowed. */
if (lengths[nchar] > FREQ_LIMIT)
lengths[nchar] = FREQ_LIMIT;

nchar = 0;
}
}

/* Now get the 'max'imal frequency in lengths[] */
for (i = 1; i <= SIZE_LIMIT; i++)
{
if (max < lengths[i])
max = lengths[i];
}

/* The last phase. Description :
- max is :
- The current frequency displayed
- The current line number
- At line 48, to display a '|' for words found more than 48
times, we must check if the frequency of this length is superior
or equal to "max".
*/

for (; max > 0; max--)
{
/* Print the current frequency displayed on this line */
printf("%3ld", max);

/* See "The Last phase" */
for (i = 1; i <= SIZE_LIMIT; i++)
if (lengths[i] >= max)
printf(" |");
else
printf(" ");

/* Prevent a scary display of the histogram */
putchar('n');
}

/* The foot of the histogram */
printf("f/l");

for (i = 1; i <= SIZE_LIMIT; i++)
printf("%3d", i);

putchar('n');
printf("f = Frequencies.n"
"l = length in chars.n");

return 0;

}


--
--
ThE_TemPLaR
Eddahbi Karim
Le #585927
Correction :


for (i = 0; i < (SIZE_LIMIT+1); i++)
for (i = 0; i =< (SIZE_LIMIT); i++)




--
--
ThE_TemPLaR

Tobias Oed
Le #585926
Eddahbi Karim wrote:

Voici une version modifiée :


[snip]

while ((c = getchar()) != EOF)
{
/* If we find a char...*/
if (c != ' ' && c != 'n' && c != 't')
{
/* And we were outside a word, we toggle "state" and
we begin to measure the length of word */
if (state == OUT)
{
state = IN;
++nchar;

/* If the word is too big, limit it to the
maximum length allowed */
if (nchar > SIZE_LIMIT)
nchar = SIZE_LIMIT+1;
}

/* Else we continue to measure */
else
++nchar;

/* Word too big : reset it to the maximum allowed */
if (nchar > SIZE_LIMIT)
nchar = SIZE_LIMIT+1;


if(state == OUT){
state = IN;
}

++n_char;

if(N_CHAR > SIZE_LIMIT){
n_char = SIZE_LIMIT;
}

Deja dans la version originale tu a ++n_char dans les 2 brances du if(). Ca
complique pour rien.
Comme j'ai dit dans ma premiere reponse tu peux te passer du test sur
state == OUT et fair le state = IN a tous les passages.
Encore plus clair:

if(state == OUT){
state = IN;
n_char = 0;
}

n_char++;

...

}

/* If we come outside a word, store the length of the word
where we came from and reset 'nchar'. (See Note 4) */
else if (state == IN)
{
state = OUT;

/* If a word is too long, warn the user */
if (nchar == SIZE_LIMIT+1)
printf("Word out of range... (%d chars)n", SIZE_LIMIT);

++lengths[nchar];


Boum.


/* If a length has been found too many times, limit it
at the maximum of occurences allowed. */
if (lengths[nchar] > FREQ_LIMIT)
lengths[nchar] = FREQ_LIMIT;

nchar = 0;
}
}

/* Now get the 'max'imal frequency in lengths[] */


La je me demende si mon message precedent est bien arrive...
a+ Tob

Poster une réponse
Anonyme