Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exercices pour débutants en C

Au menu : zSommeChiffres (nombres, algo)

Anonyme
30 novembre 2008 à 15:52:34

Bonjour,

Je suis aussi un débutant, enfin je crois :)
Je m'intéressais justement au tri par insertion, et je vois ce topic avec un exercice sur ce fameux tri qui m'a donné tant de mal!
Je sais pas trop si je peux poster mon code pour avoir différents avis dans le doute je le cache...
Voici mon code:

#include <stdio.h>
void tri(int tab[], int taille)
{
    int j, i;

    for (i=1; i < taille; i++)
    {
        for (j=0; j < i; j++)
        {
            if ( tab[i] < tab[j])
            {
                int tmpi=tab[i];
                int tmpj=tab[j];
                tab[j]=tmpi;
                tab[i]=tmpj;
            }

        }
    }
}

int main(void)
{
    int i;
    int tableau[4] = {3, 4 ,2 ,8 };

    printf("avant le tri : ");

    for (i = 0; i < 4; i++)
    {
        printf("%d ", tableau[i]);
    }


    tri(tableau, 4);

    printf("apres le tri : ");

    for (i= 0; i < 4; i++)
    {
        printf("%d", tableau[i]);
    }


    return 0;
}


Mon code est sûrement pas terrible, notamment sur les variables "tampons" à mon avis. Si vous avez des suggestions/remarques/critique à me faire n'hésitez pas, je débute et pis c'est mon premier algo de tri!


Merci.
  • Partager sur Facebook
  • Partager sur Twitter
30 novembre 2008 à 16:30:39

Salut !

Pas mal pour un premier tri, ce que tu peux faire simplement : tu n'as besoin que d'une variable pour stocker les valeurs à copier, réfléchis c'est tout simple (imagine que tu as deux boites devant toi et que tu veux les changer de place, comment tu fais?). Aussi, pourquoi crée les (ou plutôt la si tu as suivi mon premier conseil) variable dans le if? Elle sera recrée puis détruite à chaque fois que tu entres/sors du if donc ça ralentit (beaucoup je dirais) l'algo.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
30 novembre 2008 à 16:46:09

Merci pour ta réponse!

Effectivement, pour les variables tampons j'avais pas pensé qu'avec un certains ordre on n'avait besoin que d'une variable!

J'ai déclaré la variable tampon au début de la fonction, et non plus dans le if :)

Code corrigé:

#include <stdio.h>

void tri(int tab[], int taille)
{
    int j, i;
    int tmpi = 0;

    for (i=1; i < taille; i++)
    {
        for (j=0; j < i; j++)
        {
            if ( tab[i] < tab[j])
            {
                tmpi=tab[i];
                tab[i]=tab[j];
                tab[j]=tmpi;
            }
        }
    }
}

int main(void)
{
    int i;
    int tableau[4] = { 7, 13  ,2 , 21 };

    printf("avant le tri : ");

    for (i = 0; i < 4; i++)
    {
        printf(" %d ", tableau[i]);
    }

    tri(tableau, 4);

    printf("apres le tri : ");

    for (i= 0; i < 4; i++)
    {
        printf(" %d ", tableau[i]);
    }


    return 0;
}

D'autres remarques?
  • Partager sur Facebook
  • Partager sur Twitter
30 novembre 2008 à 22:18:11

Citation : Goost

Aussi, pourquoi crée les (ou plutôt la si tu as suivi mon premier conseil) variable dans le if? Elle sera recrée puis détruite à chaque fois que tu entres/sors du if donc ça ralentit (beaucoup je dirais) l'algo.


Ce que tu avance parait logique, mais d'après mes tests, il n'y a pas de différence a l'exécution. Le compilo doit optimiser cela au mieux.

Citation : Quaero

D'autres remarques?


Ton code marche bien, effectivement. :)
On peux néanmoins le critiquer, tu peux aller voir ce qu'en dit candide ici.

En gros, pour résumer la discussion ci dessus, je dirais qu'il y a deux bonnes manières d'écrire un tri par insertion :
  • soit tu commence directement l'insertion a partir de la fin de la partie de la liste déjà triée, et tu t'arrête lorsque l'élément trouve sa place. (code de candide, ici).
  • soit tu parcours la partie triée de la liste a la recherche de l'endroit ou insérer l'élément, puis tu l'insère dans un second temps.(méthode généralement plus couteuse, mais plus souple car elle permet d'optimiser le tri avec une recherche dichotomique, comme dans ce code-ci).
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
1 décembre 2008 à 21:01:13

Merci pour ta réponse yoch :)

Je suis en train de regarder le code de candide. Je dois avouer qu'il me surprend pas mal, c'est beaucoup plus subtil(et efficace) que ma méthode...

Enfin bref j'vais regarder tout ça en profondeur!
  • Partager sur Facebook
  • Partager sur Twitter
1 décembre 2008 à 21:19:01

Hello !

Je vous informe vite fait que je donnerais la correction de zStrCapitalize ainsi que celle de zAddition mercredi dans l'après-midi. ;)

@+
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 18:48:28

Correction pour zStrCapitalize



Envois des résultats à réponse : 8

L'auteur de l'exercice ne répondant plus à mes MP, j'ai décidé de poster la correction moi-même. Encore désolé pour le temps d'attente.

Dans cette exercice, il s'agissait d'implémenter un algorithme qui fait partie d'une catégorie d'algorithmes très connue : les algos appliqués sur les chaînes de caractères. L'algorithme en question, qui était votre principale tache, devait comporter pour entrée une chaine de caractères, peu importe sa taille ou son contenu. Il devait renvoyer cette même chaîne après que cette dernière ait subis quelques tortures : la première lettre devait être mise en majuscule et tout le reste en minuscule. Sous la forme d'une fonction, plusieurs solutions étaient possible quand à son prototype :
  • void zStrcap(char*);
    
    Voici le prototype le plus 'simple'. En paramètre, on envoie un pointeur sur le première char de la chaîne afin qu'on puisse bosser sur cette chaîne dans la fonction. Cette méthode comporte un (gros) défaut : Il faut que la chaîne soit au préalable initialisée, sinon l'utilisation interne à la fonction de strlen(const char*) devient tout simplement impossible car au résultat imprévisible. Certes, on n'enverra pas volontairement une chaîne non-initialisée à zStrcap mais on n'est jamais trop prudent. Ce qui nous mène à la deuxième solution, préférable d'utilisation.
  • void zStrcap(char*, size_t);
    
    Ici, on envoie tout simplement la taille du tableau en paramètre, comme cela, il n'y a pas de problème. On n'utilisera pas strlen(const char*) et l'utilisateur devra préciser la taille manuellement. ;)
  • char* zStrcap(char*, size_t);
    
    Petit modification par rapport au code précédent : ici, nous renvoyons un pointeur sur la même chaîne passée en paramètre après modification de celle-ci. Cette technique rends ainsi possible l'application de l'algorithme et l'affichage en une seule instruction, chose qui n'était pas possible avec une fonction void :
    printf(zStrcap(maChaine,taille));
    
    Pour la suite des explications, j'utiliserais ce dernier prototype, bien pratique à mon goût. ^^

Maintenant que nous avons le prototype de notre fonction zStrcap, nous allons devoir commencer à la définir. Pour cet exercice, j'ai choisi d'utiliser le charset le plus connu et le plus utilisé : ASCII. Une fois que vous avez compris le principe, vous serez en mesure d'aller encore plus loin mais utiliser un charset bien précis pour votre premier exercice de ce type est une bonne chose, je trouve. Voilà en gros l'algorithme qu'elle exécute :
  • On vérifie que le pointeur d'entrée ne vaut pas NULL. Si NULL : retourner NULL.
  • On teste la valeur ASCII du premier caractère. Si minuscule : on met en majuscule, sinon : rien.
  • On teste par itération la valeur ASCII des autres caractères. Si majuscule : on met en minuscule, sinon : rien.
  • Retourner le pointeur d'entrée.

En vous référant à la table ASCII, vous pouvez conclure que les majuscules sont situées dans l'intervalle [65;90] et les minuscule dans l'intervalle [97;122]. Pour tester la nature d'une lettre, vous pouvez donc tout simplement vérifier dans quel intervalle elle se situe. Par exemple :
char lettre /* = ... */ ;

if( lettre >= 65 && lettre <=90 )
   // majuscule...
else if( lettre >= 97 && lettre <= 122 )
   // minuscule...
else // ce n'est pas une lettre !

L'ordre d'enchaînement des lettres dans la table ASCII nous permet d'additionner une majuscule et 32 pour obtenir son équivalent minuscule et par la même logique de soustraire 32 à une minuscule pour obtenir son équivalent majuscule. Pour transformer une lettre en minuscule si elle est majuscule, on procède donc ainsi (tout simplement) :
char lettre /* = ... */ ;
if(lettre >= 65 && lettre <= 90)
    lettre += 32; // équivalent à 'lettre = lettre + 32'

Pour compléter, voici comment transformer une lettre en majuscule si elle est minuscule :
char lettre /* = ... */ ;
if(lettre >= 97 && lettre <= 122)
    lettre -= 32; // équivalent à 'lettre = lettre - 32'

Avouez que c'est très simple ! :p
À présent que le raisonnement est fait, il est grand temps de vous montrer le code complet, avec un main pour tester le tout ! Voici le code que je vous propose, tous les participants ont en gros la même chose :
char* zStrcap(char*,size_t);

int main()
{
    char txt[]={"sAluT tOI !"};
    printf(txt);
    putchar('\n');
    printf(zStrcap(txt,11));
    return 0;
}

char* zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    if(chaine != NULL && taille > 0) // on vérifie que chaine est différent de NULL et que la taille est supérieur à 0
    {
        if(chaine[0] >= 97 && chaine[0] <= 122) // pour le premier caractère, on teste s'il est minuscule
            chaine[0] -= 32; // on le met en majuscule

        for(i = 1; i<taille; i++) // pour tout le reste ...
        {
            if(chaine[i] >= 65 && chaine[i] <= 90) // on teste si chaine[i] est majuscule
                chaine[i] += 32; // on le met en minuscule
        }
    }
    return chaine;
}

La dernière touche en matière d'explication est dans le code : les commentaires. À présent si cela vous intéresse, je vous donne le code d'une version plus concise, utilisant l'opérateur ternaire et exploitant les arguments multiples pour la boucle for :
char* zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    if(chaine != NULL && taille > 0)
    {
        chaine[0] -= (chaine[0] >= 97 && chaine[0] <= 122) ? 32 : 0;
        for(i = 1; i<taille; chaine[i] += (chaine[i] >= 65 && chaine[i] <= 90) ? 32 : 0, i++);
    }
    return chaine;
}

Je posterais la correction de zAddition samedi normalement. ;)
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 18:58:58

J'ai deux/trois petits commentaires à faire si c'est possible :
-"exercice" est masculin -> "cet exercice" (bon ce commentaire là est sur la forme)
-tu ne mets pas de nom aux paramètres de tes prototypes, est-ce parce que tu citais dans l'explication ou bien? Je pense que mettre un nom (bien que facultatif) aux paramètres des prototype permet de mieux s'y retrouver (surtout pour les Zéros qui liront, c'est mieux qu'ils apprennent comme ça).
-Tu utilises l'ASCII pour arriver à tes fins, c'est une méthode non portable et qui n'est pas forcément la plus intuitive, mais bon, ceci est encore une fois subjectif et se discute, je ne veux pas de débat, juste préciser aux Zéros que cette méthode n'est pas portable.

Sinon pour le reste code propre et bien commenté, et bonne idée de montrer un "raccourci" bien utile :)
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 19:01:39

A noter que cette fonction n'est valable que pour les codages ansii.

Une manière plus portable mais non parfaite serait celle ci:
char* zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    if(chaine != NULL && taille > 0)
    {
        chaine[0] -= (chaine[0] >= 'a' && chaine[0] <= 'z') ? 32 : 0;
        for(i = 1; i<taille; chaine[i] += (chaine[i] >= 'A' && chaine[i] <= 'Z') ? 32 : 0, i++);
    }
    return chaine;
}


Mais la methode la plus portable serrait quelque chose du genre:
char zCharcap(char c)
{
   char min[] = "abcdefghijklmnopqrstuvwxyz";
   char maj[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   int taille_alphabet = 26;
   int i;
   for(i=0; i<taille_alphabet; i++)
   {
      if( c = min[i] )
          return maj[i];
   }
   return c;
}

void zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    for(i = 0; i<taille; i++)
    {
       chaine[i] = zCharcap(chaine[i]);
    }
}
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 19:22:28

Citation : Floooder

Mais la methode la plus portable serrait quelque chose du genre:

char zCharcap(char c)
{
   char min[] = "abcdefghijklmnopqrstuvwxyz";
   char maj[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   int taille_alphabet = 26;
   int i;
   for(i=0; i<taille_alphabet; i++)
   {
      if( c = min[i] )
          return maj[i];
   }
   return c;
}

void zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    for(i = 0; i<taille; i++)
    {
       chaine[i] = zCharcap(chaine[i]);
    }
}


Inutilement compliqué, je trouve... Voici ce que j'ai fait :

#include <stdio.h>

char* strcapitalise (char *str)
{
    char* ret = str;
    *str = (*str >= 'a' && *str <= 'z') ? *str-'a'+'A' : *str;
    while( (*++str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str) );
    return ret;
}

int main(void)
{
    char test[][30] = {
        "saLut les zeRos.",
        "iL Y a 3 mOts.",
        "PLOP               aux zEros."
         };
    size_t i;
    for (i=0; i<sizeof test/sizeof *test; i++)
    {
        puts(strcapitalise(test[i]));
    }
    return 0;
}
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 19:52:05

Merci pour vos commentaires ! ;)
Pour les quelques erreurs que Goost a du remarquer, j'avais édité pendant qu'il écrivait.
@yoch : ton code est très intéressant, surtout pour faire comprendre aux zér0s l'utilisation de '*' avant '++', notation qui bien souvent les fait fuir. Si tu le permets, j'édite mon message et je propose également ton code, qui, sauf sur les charset vraiment tordus, fonctionne toujours ! ^^
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:00:34

Citation

Inutilement compliqué, je trouve... Voici ce que j'ai fait :



Rien n'indique, dans l'absolu, que les lettres d'un codage quelconque se suivent et soient dans l'ordre alphabétique.
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:09:26

Citation : Floooder

Citation

Inutilement compliqué, je trouve... Voici ce que j'ai fait :



Rien n'indique, dans l'absolu, que les lettres d'un codage quelconque se suivent et soient dans l'ordre alphabétique.


Ce sujet a déjà été débattu (je ne trouve plus le topic), il est vrai que la norme ne précise pas explicitement ce point, néanmoins la fonction strcmp() utilise bel et bien cette méthode, et puis, cette méthode est largement utilisée par K&R et Cie., ce qui démontre bien que ce point est entièrement admis.

EDIT : trouvé !
http://www.siteduzero.com/forum-83-304 [...] html#r2808344
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:11:25

Ce que je sous-entendais par "charset tordus", ce sont les charset dans lesquels les lettres ne sont pas dans l'ordre alphabétiques.
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:25:40

bonjour Yoch,Flooder,
je viens de voir vos differentes façon de coder. J'avoue que je déchiffre difficilement, et notamment ton code Yoch! En tout cas je vois que l'on peut faire differemment, et cela va me faire progresser!
Je dirais que vous avez un niveau bien plus "supérieur" à nous pauvre triple zero que nous sommes :-°

Pour vous en convaincre, je vous propose quand même mon code :
il est certes plus long et moins technique que le votre (j'ai pas passé le cap
des pointeurs de fonctions) mais vos codes ne gèrent pas par exemple le fait que l'utilisateur entre comme premier caractere tout sauf une lettre !
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char ConvertiMinuscule (char caractere);
char ConvertiMajuscule (char caractere);
void strcapitalize(char tab[]);

int main()
{

    //char tab[80]=".~~+1235879    ok C est La ChAiNe A cOnVerTIR.";
    char tab[]="~~~~1~~~~aH eLle est bien boNnE CeLle la";

    printf("\n %s \nResultat :",tab);
    strcapitalize(tab);
    printf("\n %s \n \n \n",tab);

    return 0;
}

char ConvertiMinuscule (char caractere)
{
    if(caractere >= 'A' && caractere <= 'Z')
    {
        //caractere=caractere-65+97;
        caractere=caractere-'A'+'a';
    }
    return caractere;
}

char ConvertiMajuscule (char caractere)
{
    if(caractere >= 'a' && caractere <= 'z')
    {
        caractere=caractere -'a'+'A';
    }
    return caractere;
}

void strcapitalize(char tab[])
{
    int i;
    i=0;
    int condition;
    int longueurChaine;
    longueurChaine = strlen(tab);
    //si c'est pas un caractere on passe pour trouver le premier caractere de la chaine
    // il se peut que la chaine ne contienne aucun caractere
    condition = (tab[i]>='a' && tab[i]<='z') || (tab[i]>='A' && tab[i]<='Z');
    while ((condition ==0) && (i<=longueurChaine))
    {
        i++;
        condition = (tab[i]>='a' && tab[i]<='z') || (tab[i]>='A' && tab[i]<='Z');
    }
    //conversion premier caractere en majuscule

    tab[i]=ConvertiMajuscule (tab[i]);
    i++;

    // conversion des autres caracteres en minuscule
    while(i<longueurChaine)
    {
        tab[i]=ConvertiMinuscule(tab[i]);
        i++;
    }


}


Merci
@+


  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:41:51

Citation : zx-spectrum

Je dirais que vous avez un niveau bien plus "supérieur" à nous pauvre triple zero que nous sommes :-°


Une chance pour vous, vous pouvez beaucoup apprendre. ;)

Citation : zx-spectrum

mais vos codes ne gèrent pas par exemple le fait que l'utilisateur entre comme premier caractere tout sauf une lettre !


Si le premier caractère n'est pas une lettre, on le laisse tel quel ! :o Qu'entends-tu par "ne gèrent pas" ?
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:48:11

Citation : zx-spectrum

bonjour Yoch,Flooder,
je viens de voir vos differentes façon de coder. J'avoue que je déchiffre difficilement, et notamment ton code Yoch!



Désolé, je te dois alors quelques explications :

*str correspond a : str[0] , puisque c'est le contenu de la variable pointée par str, str etant un pointeur sur le premier caractère d'une chaine.

*++str se décompose ainsi (question de priorité d'opérateurs) : on incrémente le pointeur de une case, puis on évalue la valeur de la variable pointée par str (parce que l'opérateur ++ se trouve avant l'expression str, dans le cas inverse, c'est l'inverse)...

@+
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:49:46

bonjour chrys:
par exemple :
en entrée:
"       cOuCou"
en sortie :
"       Coucou"
mon programme le gère!
@+

ps: merci yoch pour tes explications supplémentaires, je te rassure si j'ai besoin je n'hesites pas a demander!
De plus je ne l'ai pas dit et je le pense : le fait de voir un niveau de connaissance plus eleve que le sien est utile et bien. Donc Merci de nous apprendre à faire mieux!
@+
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 20:58:31

Tu as raison, mais cela se fait facilement avec une fonction qui repère la première lettre dans la chaîne et qui renvoie son emplacement, genre (non-testé!) :
#include <ctype.h>

int getpos(char* c, size_t t)
{
    int pos;
    for(pos = 0; pos<t && !isalpha(c[pos]); pos++);
    return pos;
}

On pourrait alors l'utiliser ainsi (non-testé!) :
char* zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    if(chaine != NULL && taille > 0) // on vérifie que chaine est différent de NULL et que la taille est supérieur à 0
    {
        i = getpos(chaine, taille);
        if(chaine[i] >= 97 && chaine[0] <= 122) // pour le premier caractère, on teste s'il est minuscule
            chaine[i] -= 32; // on le met en majuscule

        for(i++; i<taille; i++) // pour tout le reste ...
        {
            if(chaine[i] >= 65 && chaine[i] <= 90) // on teste si chaine[i] est majuscule
                chaine[i] += 32; // on le met en minuscule
        }
    }
    return chaine;
}
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 21:08:11

Si tu l'as remarqué dans ma version je l'ai codé comme cela :
//si c'est pas un caractere on passe pour trouver le premier caractere de la chaine
    // il se peut que la chaine ne contienne aucun caractere
    condition = (tab[i]>='a' && tab[i]<='z') || (tab[i]>='A' && tab[i]<='Z');
    while ((condition ==0) && (i<=longueurChaine))
    {
        i++;
        condition = (tab[i]>='a' && tab[i]<='z') || (tab[i]>='A' && tab[i]<='Z');
    }


--> car je ne connaissais pas la fonction jusqu'a ce soir: isalpha(caractere) ! Un truc de plus dans ma bibliotheque de mes connaissances :p
merci
@+
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 22:06:45

Citation : Floooder

A noter que cette fonction n'est valable que pour les codages ansii.



ansii ? non ASCII.

Citation : Floooder


Une manière plus portable mais non parfaite serait celle ci:



C'est plus lisible mais pas plus portable. En plus, tu as un nombre magique (32) ce qu'il faut éviter, utiliser à la place une macro, genre

#define ECART ('a'-'A')




Quelques remarques sur ton code :

Citation : Floooder


if(chaine != NULL && taille > 0)




Je ne vois pas comment une chaine (au sens de la norme) peut être NULL. En outre, une chaine réduite à '\0' ne pose pas de problème particulier.

Citation : Floooder


chaine[0] -= (chaine[0] >= 'a' && chaine[0] <= 'z') ? 32 : 0;



Complètement artificiel parce que retirer 0 à un nombre franchement ...

Citation : Floooder


for(i = 1; i<taille; chaine[i] += (chaine[i] >= 'A' && chaine[i] <= 'Z') ? 32 : 0, i++);



Dans le genre code illisible, et complètement artificiel et qui utilise le processeur pour retirer 0. Je veux bien qu'on fasse du code abscon mais quand vraiment ça apporte quelque chose mais là ...

En plus, tu utilises à deux reprises

(chaine[0] >= 'a' && chaine[0] <= 'z') ? 32 : 0


tu aurais dû écrire une macro ou une fonction car on essaye d'éviter la redondance de code.


Citation : Floooder


Mais la methode la plus portable serrait quelque chose du genre:

char zCharcap(char c)
{
   char min[] = "abcdefghijklmnopqrstuvwxyz";
   char maj[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   int taille_alphabet = 26;
   int i;
   for(i=0; i<taille_alphabet; i++)
   {
      if( c = min[i] )
          return maj[i];
   }
   return c;
}

void zStrcap(char* chaine, size_t taille)
{
    unsigned i;
    for(i = 0; i<taille; i++)
    {
       chaine[i] = zCharcap(chaine[i]);
    }
}


Portable peut-être mais les performances sont piètres. Pour chaque recherche de chaine[i], on doit parcourir tout l'alphabet avant de tomber sur la lettre (en supposant que ce soit une lettre sinon on l'a parcouru pour rien ce qui pouvait facilement être évité avec un petit test préalable). Ce qu'il fallait faire, c'est construire un moyen d'accès direct avec un tableau indexé de 0 à SCHAR_MAX et qui renvoie le code de la majuscule correspondant.



Citation : yoch


*str = (*str >= 'a' && *str <= 'z') ? *str-'a'+'A' : *str;


Pareil, je trouve ça complètement artificiel, on ne va pas affecter quelque chose à lui-même. Pourquoi vouloir à tout prix utiliser l'opérateur ternaire, pourquoi pas un simple if sans else ? En outre, ci-dessus, *str est évalué 3 fois.


Citation : yoch


while( (*++str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str) );


Je n'en suis pas sûr à 100% mais je pense que ce code est incorrect. En effet l'ordre des opérations n'est pas spécifié par la norme (sauf certains opérateurs mais pas pour l'affectation) et donc il se pourrait très bien que l'incrémentation de str se fasse avant le test ternaire auquel cas tu n'obtiendras pas le résultat souhaité. Par ailleurs, je trouve cette boucle while peu lisible et peu économique (*str est évalué 3 fois et à de nombreuses reprises on va affecter une variable à elle-même).

  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 22:22:06

Citation : candide


Citation : yoch


*str = (*str >= 'a' && *str <= 'z') ? *str-'a'+'A' : *str;



Pareil, je trouve ça complètement artificiel, on ne va pas affecter quelque chose à lui-même. Pourquoi vouloir à tout prix utiliser l'opérateur ternaire, pourquoi pas un simple if sans else ? En outre, ci-dessus, *str est évalué 3 fois.


J'admets pour la première remarque. Mais je ne comprends pas trop ce que tu veux dire par la seconde : '*str est évalué 3 fois'. Je me sert de *str comme d'une variable classique, je ne pense pas que cela soit plus couteux, car tout accès a une variable passe par son adresse.

Citation : candide


Citation : yoch


while( (*++str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str) );



Je n'en suis pas sûr à 100% mais je pense que ce code est incorrect. En effet l'ordre des opérations n'est pas spécifié par la norme (sauf certains opérateurs mais pas pour l'affectation) et donc il se pourrait très bien que l'incrémentation de str se fasse avant le test ternaire auquel cas tu n'obtiendras pas le résultat souhaité.


Bien au contraire, je compte justement sur la priorité de l'opérateur ++ (situé avant l'expression) pour incrémenter str AVANT le test, ce qui revient a faire :
while (str++)
   *str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str;

Maintenant, il se peut comme tu le pense que ce soit incorrect car indéterminé, a vérifier donc.

Et puis bon, j'avoue avoir voulu économiser des lignes de code, je sais, c'est mal...
  • Partager sur Facebook
  • Partager sur Twitter
3 décembre 2008 à 23:19:22

Citation : yoch


Mais je ne comprends pas trop ce que tu veux dire par la seconde : '*str est évalué 3 fois'. Je me sert de *str comme d'une variable classique, je ne pense pas que cela soit plus couteux, car tout accès a une variable passe par son adresse.



Peut-etre mais *str comporte un déréférencement, donc ça a un cout à l'exécution, je me dit que c'est moins rapide qu'une simple lecture de c où tu as posé c=*str. Mais bon, c'est vrai que ça doit pas jouer beaucoup. Je vois assez souvent du code pro ecrit comme tu l'as fait donc c'est peut-être pas si grave.


Citation : yoch


Bien au contraire, je compte justement sur la priorité de l'opérateur ++ (situé avant l'expression) pour incrémenter str AVANT le test, ce qui revient a faire :


Oui, en effet, il te faut incrémenter avant le test pour obtenir l'effet que tu souhaites. Mais qu'est-qui t'assure qu'il en sera ainsi ? Dans la norme, rien. Il se peut, à l'inverse de ce que j'ai dit dans le précédent message, que le test ternaire soit effectué d'abord (au passage, pour l'opérateur ternaire, l'ordre d'évaluation des opérateurs est défini par la norme) et donc le membre de droite n'aura peut-être pas la valeur que tu souhaites.

Par contre, ce qui m'étonne c'est que le compilateur ne t'ait rien dit.

D'un autre coté, tu aurais pu écrire :

while( str++, (*str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str) );


tout aussi bien que

while( ++str, (*str = (*str >= 'A' && *str <= 'Z') ? *str-'A'+'a' : *str) );


mais c'est encore plus illisible.



Citation : yoch


Et puis bon, j'avoue avoir voulu économiser des lignes de code,



Heu, oui, c'était très visible mais on l'a tous fait. Le C pousse au crime ;)
  • Partager sur Facebook
  • Partager sur Twitter
4 décembre 2008 à 0:32:57

Citation

Quelques remarques sur ton code :


En fait candide, dans mon premier code, j'ai juste remplacé les nombres magiques des lettres du code de la correction par les char. C'etait principalement pour montrer qu'on peut et qu'il vaut mieux utiliser les char à la place de leurs valeurs.

Mais sinon je ne l'aurais pas vraiment codé comme ca. Une version propre et aérée (pas de compression d'ecriture de code) pourrait être :
void zStrcap( char * chaine )
{
   while ( *chaine )
   {
      if (  *chaine >='a' && *chaine <= 'z' )
      {
         *chaine += 'A'-'a';
      }
      chaine++;
   }
}


Sinon tu as parfaitement raison quand tu dis ceci :

Citation

Ce qu'il fallait faire, c'est construire un moyen d'accès direct avec un tableau indexé de 0 à SCHAR_MAX et qui renvoie le code de la majuscule correspondant.

  • Partager sur Facebook
  • Partager sur Twitter
4 décembre 2008 à 0:38:42

Citation : Floooder


En fait candide, dans mon premier code, j'ai juste remplacé les nombres magiques des lettres du code de la correction par les char. C'etait principalement pour montrer qu'on peut et qu'il vaut mieux utiliser les char à la place de leurs valeurs.


OK, c'est vrai que tu avais repris le code de crys.


Citation : Floooder


void zStrcap( char * chaine )
{
   while ( *chaine )
   {
      if (  *chaine >='a' && *chaine <= 'z' )
      {
         *chaine += 'A'-'a';
      }
      chaine++;
   }
}




Tout à fait.
  • Partager sur Facebook
  • Partager sur Twitter
4 décembre 2008 à 1:26:12

Pour certains, il faudrait revoir la difference entre capitalize, to upper et/ou une phrase.

Sinon:

#include <stdlib.h>                                                             
#include <stdio.h>                                                              
                                                                                
#define DIFF    ('A' - 'a')                                                     
#define ISCHAR(x) ((x >= 'a' && x <= 'z') || (x >='A' && x <= 'Z') ? 1 : 0)      
                                                                                
char    *zStrcap(char *str);                                                    
                                                                                
int     main(int ac, char **av)                                                 
{                                                                               
  if (ac == 2)                                                                  
    printf("%s\n", zStrcap(av[1]));                                             
  return (EXIT_SUCCESS);                                                        
}                                                                               
                                                                                
char    *zStrcap(char *str)                                                     
{                                                                               
  int   i;                                                                      
                                                                                
  i = 0;                                                                        
  while (str && str[i])                                                         
    {                                                                           
      if ((!i || !ISCHAR(str[i - 1])) && str[i] >= 'a' && str[i] <= 'z')        
        str[i] += DIFF;                                                         
      if (i && ISCHAR(str[i - 1]) && str[i] >= 'A' && str[i] <= 'Z')            
        str[i] -= DIFF;                                                         
      ++i;                                                                      
    }                                                                           
  return (str);                                                                 
}
  • Partager sur Facebook
  • Partager sur Twitter
4 décembre 2008 à 19:11:08

le « ? 1 : 0 » est un peu inutile non ?
  • Partager sur Facebook
  • Partager sur Twitter
4 décembre 2008 à 22:06:26

Effectivement ce n'est pas ce qui etait demandé...

Maintenant si c'est seulement la première lettre de chaque phrase, je ferrai quelque chose comme ca :

char zTolower(char c)
{
   if ( c >='A' && c <= 'Z' )
      return c-'A'+'a';
   return c;
}

char zToupper(char c)
{
   if ( c >='a' && c <= 'z' )
      return c-'a'+'A';
   return c;
}

int zIsendsent(char c)
{
   return !c || c=='.' || c=='?' || c=='!';
}

int zIsspace(char c)
{
   /* a completer avec les caractères de saut de pages et autres exotiques */
   return c && ( c==' ' || c=='\t' || c=='\n' );
}

char * zStrcap(char * chaine)
{
   c = chaine;
   /* pour chaque phrase de la chaine */
   while(*c)
   {
      /* on avance jusqu'a premier caractère different de l'espace */
      while( zIsspace(*c) )
      {
         c++;
      }
      /* On met le caractère en majuscule si on n'est pas a la fin de la chaine */
      if ( !zIsendsent(*c) )
      {
         *c = zToupper(*c);
         c++;
         /* On met les autres caractères en minuscule */
         while( !zIsendsent(*c) )
         {
             *c = zToloxer( *c );
             c++;
         }
      }
      c++;
   }
   return chaine;
}
  • Partager sur Facebook
  • Partager sur Twitter