Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exercices pour débutants en C

Au menu : zSommeChiffres (nombres, algo)

20 décembre 2008 à 20:11:01

Bonjour Candide,
il est toujours difficile de proposer quelque chose. Je reconnais pour ma part que cet exo est particulièrement "hardu", et pas forcément à la portée du réel débutant.....
Neamoins, vu que personne se manifeste pour savoir ce que propose Chrys est à leur portée, donc pas de feedback, je pense qu'il fait ce qu'il peut pour nous contenter......
Pour ma part et cela n'engage que moi, j'encourage chrys à continuer à nous proposer des exos. S'ils ne sont pas adaptés à notre niveau, a nous a nous manifester, car j'ai pas vu de zeros comme moi dire : ah halte , je n'arrive pas à suivre.....
Ensuite à chacun à faire un effort, et au moins essayer à faire quelque chose, a proposer sa solution.
@+
  • Partager sur Facebook
  • Partager sur Twitter
21 décembre 2008 à 15:08:50

Citation : de zx-spectrum

S'ils ne sont pas adaptés à notre niveau, a nous a nous manifester, car j'ai pas vu de zeros comme moi dire : ah halte , je n'arrive pas à suivre.....
Ensuite à chacun à faire un effort, et au moins essayer à faire quelque chose, a proposer sa solution.


Si, je l'ai dit, et je crois que mon appel à été pris en compte.
  • Partager sur Facebook
  • Partager sur Twitter
21 décembre 2008 à 15:46:09

Le prochain exercice sera très simple (et donc collera avec le titre). ^^ J'ai déjà rédigé l'énoncé.
  • Partager sur Facebook
  • Partager sur Twitter
21 décembre 2008 à 18:02:25

Citation : candide

Par ailleurs, l'exercice en soi est difficile pour des débutants. La preuve ? toute simple : le code dans K&R qui implémente cette question (§5.10 Command-line Arguments) est très long : environ 125 lignes mais une ligne de C de K&R vaut 3 à 5 lignes chez le débutant, donc on va compter 500 lignes et Kernighan est un maitre en algorithmique donc le codage du débutant sera encore plus long. Noter que les formules doivent tenir dans des tableaux statiques ce qui un grosse limitation et dont la résolution nécessiterait encore plus de lignes de code.


A mon avis, ce genre de preuve n'est pas valable : le nombre de lignes de code n'est aucunement indice de difficulté.

Et puis, n'oublions pas l'énoncé :

Citation : Pas de titre

Pour la dernière question, utiliser seulement les chiffres dans vos calculs et non les nombres à plus d'un chiffre. Pour lire un nombre dans une chaîne, c'est déjà un algorithme tout à part (pas très dur mais faites juste ce qui est demandé), tandis que récupérer un chiffre est très simple (on n'a même pas à réfléchir) et vous aurez tout le temps pour vous concentrer sur l'algorithme de lecture d'expressions en NPI, ce qui est le but de l'exercice.



Citation : candide

Et le pire, ils se sont plantés.
[...]

Maintenant deux hics :

1°) le programme ne gère pas les formules invalides et pire il répond quelque chose :

candide@candide-desktop:~$ ./x
100000 1 1 * + 1 1 1 + +
	3



2°) Absolument incroyable, le programme ne sait pas gérer les nombre négatifs et le passage à l'opposé, exemple :

candide@candide-desktop:~$ ./x
2 3 + -
error: stack empty
	-5
2 3 + - 2 3 + - *
error: stack empty
error: stack empty
	-0
2 -3 -
error: stack empty
	-5




Je suis d'accord, ces erreurs sont assez stupéfiantes (pour le passage a l'opposé, encore fallait-il savoir que c'est autorisé, mais pour le reste... a moins que le code ne vise d'autres objectif que la précision algorithmique, et encore !).

Maintenant, tout cela est en dehors du cadre de l'exercice tel que défini par l'énoncé, qui était très simple je trouve, et assez instructif globalement (pile, npi).

Vis a vis de la simplicité du code dans les cas un peu plus complets que demandé, je trouve que le conseil que j'avais donné (utiliser strtol) était excellent. Voici mon code (les espaces font office de séparateurs, gère les nombres négatifs, gère les erreurs : expression invalide, division par zéro) :


#include <stdio.h>
#include <stdlib.h>

/** implémentation de pile provenant du tuto de Octal
http://www.siteduzero.com/tutoriel-3-34229-les-piles-et-les-files.html */

typedef struct pile
{
    int value;
    struct pile *precedent;
} Pile;

void pile_push (Pile **p_pile, int value)
{
    Pile *p_nouveau = malloc(sizeof *p_nouveau);
    if (p_nouveau != NULL)
    {
        p_nouveau->value = value;
        p_nouveau->precedent = *p_pile;
        *p_pile = p_nouveau;
    }
}

int pile_pop (Pile **p_pile)
{
    int ret = 0;
    if (*p_pile != NULL)
    {
        Pile *temporaire = (*p_pile)->precedent;
        ret = (*p_pile)->value;
        free(*p_pile), *p_pile = NULL;
        *p_pile = temporaire;
    }
    return ret;
}

void pile_clear(Pile **p_pile)
{
    while (*p_pile != NULL)
    {
        pile_pop(p_pile);
    }
}
/*********************************************************/

int isOperator (char c)
{
    return (c == '+' || c == '-' || c == '*' || c == '/');
}

int main(void)
{
    Pile *pile = NULL;
    char str[] = "-4 5 * -10 +";
    char *expr = str, *ptr;
    int a, b, val;
    /* compteur pour l'etat de la pile */
    int n = 0, divisionParZero = 0;

    do
    {
        val = strtol (expr, &ptr, 10);
        /* si strtol a lu un nombre */
        if ( expr < ptr )
        {
            expr = ptr;
            pile_push (&pile, val);
            n++;
        }
        /* sinon si on lit un operateur */
        else if ( isOperator (*expr) )
        {
            a = pile_pop(&pile);
            b = pile_pop(&pile);
            switch (*expr)
            {
            case '+':
                pile_push (&pile, b + a);
                break;
            case '-':
                pile_push (&pile, b - a);
                break;
            case '*':
                pile_push (&pile, b * a);
                break;
            case '/':
                if (a != 0)
                    pile_push (&pile, b / a);
                else
                    divisionParZero = 1;
                break;
            }
            n--;
        }
    }
    while (*++expr && !divisionParZero);

    /* Si la pile contient un seul element */
    if (n == 1 && !divisionParZero)
    {
        printf ("'%s' vaut : %d\n", str, pile_pop(&pile));
    }
    else
    {
        if (divisionParZero)
        {
            puts("Erreur : division par zero !");
        }
        else
        {
            puts("Erreur : l'expression est erronnee !");
        }
        /* On vide la pile */
        pile_clear(&pile);
    }
    return 0;
}

EDIT : ajout de la gestion de division par zéro.
  • Partager sur Facebook
  • Partager sur Twitter
21 décembre 2008 à 19:36:46

Citation : L'Ombre Blanche

Citation : de zx-spectrum

S'ils ne sont pas adaptés à notre niveau, a nous a nous manifester, car j'ai pas vu de zeros comme moi dire : ah halte , je n'arrive pas à suivre.....
Ensuite à chacun à faire un effort, et au moins essayer à faire quelque chose, a proposer sa solution.


Si, je l'ai dit, et je crois que mon appel à été pris en compte.


Ok , je te rassure on est au moins deux alors :
!
@+
  • Partager sur Facebook
  • Partager sur Twitter
21 décembre 2008 à 22:56:14

Citation : yoch


A mon avis, ce genre de preuve n'est pas valable : le nombre de lignes de code n'est aucunement indice de difficulté.



Bien sûr que si. En général, un programme de 500 lignes est plus complexe (!= difficile) qu'un programme qui en fait 30. Bien sûr je parle dans les cas usuels, pas de code de l'IOCCC. Ce qui ne veut pas dire que certains codes astucieux et très travaillés de 30 lignes soient moins faciles à concevoir que des codes faisant la même chose en 100 lignes.

Citation : yoch


Et puis, n'oublions pas l'énoncé :


Quel rapport ? Ce que le code de K&R fait, c'est exactement la même chose (la seule différence étant qu'on prend des nombres à un chiffre dans le zExo sinon, c'est pareil : npi et pile).

Citation : yoch


Voici mon code (les espaces font office de séparateurs, gère les nombres négatifs, gère les erreurs : expression invalide, division par zéro) :



Et tu crois que n'importe quel débutant va déjà simplement comprendre ton code ? Ton code est très difficile, il nécessite la maitrise de pas mal de choses non triviales en C, c'est du code ABOUTI, le contraire de ce que sait faire un débutant.


Par ailleurs, ton programme, à l'entrée invalide :
2 3 + - 2 3 +


répond 5.

Voici le code que je propose :

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  char *p = "1 2 + 3 4 * 1 5 3 2 - * - 2 * + 1 * - 3 -",*q=p;
  int pile[100] = { 0 };
  int hauteur = 0;

  while (*p)
    {
      switch (*p)
        {
        case ' ':
          break;
        case '+':
          pile[hauteur - 2] += pile[hauteur - 1];
          hauteur--;
          break;
        case '-':
          pile[hauteur - 2] -= pile[hauteur - 1];
          hauteur--;
          break;
        case '*':
          pile[hauteur - 2] *= pile[hauteur - 1];
          hauteur--;
          break;
        case '/':
          if (pile[hauteur] == 0)
            return 0;
          else
            {
              pile[hauteur - 2] /= pile[hauteur - 1];
              hauteur--;
            }
          break;
        default:
          pile[hauteur] = *p - '0';
          hauteur++;
        }
      p++;
    }

  printf("%s vaut %d\n", q, pile[0]);
  return 0;
}

1 2 + 3 4 * 1 5 3 2 - * - 2 * + 1 * - 3 - vaut -4


Mon code ne gère pas les expressions invalides (mais ce n'est pas très difficile à ajouter).

  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 9:12:08

salut tout le monde moi aussi je participerai à ce sujet car je veux bien apprendre le C
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 9:50:25

Citation : Crys

Exercices pour débutants en C

Citation : smail26

...moi aussi je participerai à ce sujet car je veux bien apprendre le C



Et bien, entre des discours de gourous d'experts et des interventions de boulets p'tits nouveaux, comment un débutant peut-il s'y retrouver ? :lol:
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 10:30:20

Citation : stallaf

Citation : Crys

Exercices pour débutants en C

Citation : smail26

...moi aussi je participerai à ce sujet car je veux bien apprendre le C



Et bien, entre des discours de gourous d'experts et des interventions de boulets p'tits nouveaux, comment un débutant peut-il s'y retrouver ? :lol:



Comme quoi, ce n'est pas si évident de proposer les bons exercices d'apprentissage adaptés au public visé, c'est même un vrai boulot, ça s'appelle enseignant et meme les enseignants ne parviennent pas souvent à un résultat satisfaisant. La production d'un petit exo facile pour débutant peut nécessiter des heures de conception, mais tout dépend du degré de rigueur du concepteur.

Ici, les exos sont des difficultés inégales, ils s'adressent à un public au savoir trop indéterminé, leur contenu hésite trop entre codage C et algorithmique. Et la rubrique n'a pas encore réussi à fidéliser un public substantiel de zéros, à la différence par exemple du vrai succès des sujets de Nanoc (qui sont en C++ mais c'est dommage qu'il n'y ait pas la meme rubrique mais en C). AMHA, si les zexos veulent cibler un public de débutants, il faut absolument limiter la difficulté de ces exercices, éviter toute question d'algorithmique pure, soigner les spécifications et surveiller la précision des énoncés.
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 10:37:46

Citation : stallaf

Citation : Crys

Exercices pour débutants en C

Citation : smail26

...moi aussi je participerai à ce sujet car je veux bien apprendre le C



Et bien, entre des discours de gourous d'experts et des interventions de boulets p'tits nouveaux, comment un débutant peut-il s'y retrouver ? :lol:


-sauf que les petits nouveaux je n'en ai vu que deux qui se manifestent...
-le discours de gourous comme tu dis, heureusement qu'ils sont la pour nous faire progresser, il suffit juste a un moment donné de leur signaler : stop j'ai rien compris, explications supplémentaires requises, et ils le font (merci au passage pour eux)

Le repérage me parait aisé pour les nouveaux, il suffit de réaliser les deux points ci-dessus.


Citation : candide

Comme quoi, ce n'est pas si évident de proposer les bons exercices d'apprentissage adaptés au public visé, c'est même un vrai boulot, ça s'appelle enseignant et meme les enseignants ne parviennent pas souvent à un résultat satisfaisant. La production d'un petit exo facile pour débutant peut nécessiter des heures de conception, mais tout dépend du degré de rigueur du concepteur.

Ici, les exos sont des difficultés inégales, ils s'adressent à un public au savoir trop indéterminé, leur contenu hésite trop entre codage C et algorithmique. Et la rubrique n'a pas encore réussi à fidéliser un public substantiel de zéros, à la différence par exemple du vrai succès des sujets de Nanoc (qui sont en C++ mais c'est dommage qu'il n'y ait pas la meme rubrique mais en C). AMHA, si les zexos veulent cibler un public de débutants, il faut absolument limiter la difficulté de ces exercices, éviter toute question d'algorithmique pure, soigner les spécifications et surveiller la précision des énoncés



--> plutôt d'accord, mais il serait bon de savoir le niveau du public qui participe !
pour ma part : débutant, 4 mois d'apprentissage du C
@+
@+

  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 11:31:11

Citation : zx-spectrum


pour ma part : débutant, 4 mois d'apprentissage du C


Ce n'est pas une information vraiment exploitable : ton niveau dépend de ton background, de ton mode d'apprentissage, de la durée quotidienne que tu y passes (moi, quand je me suis lancé dans le C, TOUT mon temps libre y passait), etc. Par exemple, tu as assimilé jusqu'à quel chapitre du cours officiel du sdz ?
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 11:32:36

Bonjour,

Je viens vous proposer un exercice.
Dans la prise en main de la SDL, je viens rajouter un exercice qui ne ferait pas une si mauvaise transition pour le TP du Sokoban: L'usage des tableau 2D.
En effet, je vous propose de créer un damier par manipulation de surfaces.
Une façon de faire ce code est d'utiliser le tableau à deux dimensions.
Mon code ressemblerait à celui des dégradés proposés par M@téo21 dans son tutoriel... (je n'en dis pas plus, je pense que c'est largement suffisant!!!)

Je vous proposerai ma solution mardi 23 Décembre!!!
Ok??? Savez-vous pour quoi? C'est parce qu'il y a mieux comme cadeau pour vous à Noël!!!
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 12:31:43

Citation : zerozeroun


Dans la prise en main de la SDL,



A mon avis, dans cette rubrique, il faut totalement exclure tout ce qui n'est pas du C standard, en particulier la SDL.
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 13:44:27

Exactement, je m'auto-cite :

Citation : premier post

aucune installation d'une bibliothèque non-standard ne sera nécessaire.


De plus, pour l'utilisation des tableaux 2D, il y a zAddition. ;)
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 19:46:39

Et c'est giga beaucoup trop compliqué pour moi. :-°
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 19:51:30

Citation : L'Ombre Blanche

Et c'est giga beaucoup trop compliqué pour moi. :-°



Qu'est-ce qui est trop compliqué pour toi ? La NPI ? Où en es-tu dans ton apprentissage du C (quel chapitre dans le cours du sdz) ?
  • Partager sur Facebook
  • Partager sur Twitter
22 décembre 2008 à 20:27:09

J'ai buggé au cours de pointeurs, j'ai lu la suite, mais je ne l'ai pas assimilée faute de pratique.
Voici la raison de ma présence sur ce forum.
  • Partager sur Facebook
  • Partager sur Twitter
23 décembre 2008 à 0:44:11

Citation : L'Ombre Blanche

J'ai buggé au cours de pointeurs



j'ai beaucoup souffert aussi donc je compatis ;)
  • Partager sur Facebook
  • Partager sur Twitter
23 décembre 2008 à 18:03:11

Je pense que pour les pointeurs, l'ideal c'est de vraiment les voir comme des adresses machines, personnellement ca m'a beaucoup aidé ;)
  • Partager sur Facebook
  • Partager sur Twitter
28 décembre 2008 à 1:19:40

bonjour,

Citation : L'Ombre Blanche

J'ai buggé au cours de pointeurs, j'ai lu la suite, mais je ne l'ai pas assimilée faute de pratique.
Voici la raison de ma présence sur ce forum.



-->je bugge moi même encore.....et je suis obligé de regarder mes petites fiches pour comprendre ce que je fais.
--> des exos sur ce sujet je suis preneur....

j'en profite au passage pour vous souhaiter à tous de joyeuses fêtes de fin d'année ! :)
@+
  • Partager sur Facebook
  • Partager sur Twitter
3 janvier 2009 à 21:23:17

La correction est en cours de rédaction. Je manque malheureusement de temps mais elle sera publiée bientôt.
  • Partager sur Facebook
  • Partager sur Twitter
10 janvier 2009 à 13:30:13

Moi, c'est surtout les algorithmes qui me pose un problème :euh::euh: . Ce serait bien qu'il soient un peu plus simple.
  • Partager sur Facebook
  • Partager sur Twitter
11 janvier 2009 à 18:52:52

Correction pour zMath



Envois des résultats à réponse : 6

Voilà enfin la correction de l'exercice zMath avec un léger retard. :-° J'ai été très occupé ces temps-ci. C'est parti. ^^

Pour mener à bien cet exercice, il fallait apprendre à manipuler les piles. Pour cette correction, je vais reprendre les fonctions mises à disposition par Octal, je les trouve très biens et il y a tout juste ce qu'il nous faut.

Les inclusions



Commençons par le plus rapide : les inclusions. Réfléchissons ! Nous allons naturellement inclure stdio.h pour la saisie d'un calcul NPI et l'affichage du résultat. Ensuite, il nous faudra aussi inclure stdlib.h afin de pouvoir utiliser free et malloc, enfin, ces fonctions seront appelées dans les fonctions que je récupère du tuto d'Octal.
#include <stdio.h>
#include <stdlib.h>


La manipulation des piles



Bien entendu, il nous faudra récupérer la structure que propose Octal qui nous servira à construire une pile :
typedef struct pile
{
    int donnee;
    struct pile *precedent;
} Pile;

Ensuite, on va devoir pouvoir ajouter des éléments à la pile :
void pile_push(Pile **p_pile, int donnee)
{
        Pile *p_nouveau = malloc(sizeof *p_nouveau);
        if (p_nouveau != NULL)
        {
                p_nouveau->donnee = donnee;
                p_nouveau->precedent = *p_pile;
                *p_pile = p_nouveau;
        }
}

Enfin, l'opération inverse, c'est à dire le retrait d'un élément :
int pile_pop(Pile **p_pile)
{
    int ret = -1;
    if (p_pile != NULL)
    {
        Pile *temporaire = (*p_pile)->precedent;
        ret = (*p_pile)->donnee;
        free(*p_pile), *p_pile = NULL;
        *p_pile = temporaire;
    }
    return ret;
}

On va même récupérer la fonction pour détruire une pile dans sa totalité. Elle sera très utile dans le cas où l'expression entrée par l'utilisateur de zMath est invalide ce qui pourrait entrainer une fuite de mémoire. Voici la fonction en question :
void pile_clear(Pile **p_pile)
{
    while (*p_pile != NULL)
        pile_pop(p_pile);
}


La fonction zMath()



Maintenant, nous abordons la partie algorithmique. Nous allons construire la fonction qui va nous permettre d'interpréter des expressions NPI et qui va nous retourner le résultat du calcul. Commençons par le prototype : la fonction renverra un entier et prendra en paramètre un pointeur sur la chaîne à évaluer ainsi que la taille de la chaîne (strlen() n'est pas une fonction toujours efficace). On obtient ceci :
int zMath(const char* calcul, size_t taille);

Passons à la déclaration des variables. Il nous faudra bien sûr un objet de type Pile, une variable itérateur pour parcourir la chaîne (que nous allons nommer i :p ) et trois autres variables qui vont nous servir lors de nos calculs et, à la fin, pour renvoyez le résultat.
int i, temp1, temp2, final;
Pile* ma_pile = NULL;
i = temp1 = temp2 = final = 0;

(Prenez l'habitude d'initialiser vos variables, c'est toujours bon)
Ensuite, nous allons construire une boucle for afin d'itérer sur la chaîne à évaluer :
for(; i < taille; i++){}

Le code que nous allons construire par la suite sera naturellement à placer à l'intérieur de la boucle.
Que faire maintenant ? C'est bien simple : il faut tester la nature du caractère lu à chaque tour de boucle. Six cas de figures sont possibles :
  • On a affaire à un chiffre. Dans ce cas, si vous avez bien compris le principe de la NPI, vous devriez savoir qu'il nous suffit d'ajouter ce chiffre dans la pile :
    if(calcul[i] >= '0' && calcul[i] <= '9') // si c'est un chiffre...
        pile_push(&ma_pile,calcul[i]-'0');
        // '-0' permet de "convertir" l'information numérique
        // d'un caractère en "vrai" chiffre
    
  • On a affaire au caractère '*' (fois). On multiplie les deux derniers éléments de la pile entres eux, tout en les retirant, et on ajoute le résultat à la pile :
    else if(calcul[i] == '*')
    { // multiplication
        temp1 = pile_pop(&ma_pile);
        temp2 = pile_pop(&ma_pile);
        pile_push(&ma_pile, temp2*temp1);
    }
    
  • Idem pour la division :
    else if(calcul[i] == '/')
    { // division
        temp1 = pile_pop(&ma_pile);
        temp2 = pile_pop(&ma_pile);
        pile_push(&ma_pile, temp2/temp1);
    }
    
  • Idem pour l'addition :
    else if(calcul[i] == '+')
    { // addition
        temp1 = pile_pop(&ma_pile);
        temp2 = pile_pop(&ma_pile);
        pile_push(&ma_pile, temp2+temp1);
    }
    
  • Et encore idem pour la soustraction :p :
    else if(calcul[i] == '-')
    { // soustraction
        temp1 = pile_pop(&ma_pile);
        temp2 = pile_pop(&ma_pile);
        pile_push(&ma_pile, temp2-temp1);
    }
    
  • L'utilisateur peut aussi avoir saisi des caractères invalides (ici, il s'agit d'un caractère n'étant pas un chiffre ni un opérateur arithmétique). Dans ce cas, on affiche un beau message d'erreur :
    else // caractère invalide
        printf(" Erreur : %c n'est pas considéré comme un caractere valide ! \n", calcul[i]);
    

Ce sera tout pour le contenu de la boucle for, vous pouvez donc la fermer ( :p:p ). Il nous reste à renvoyer le résultat. Mais pas si vite ! Il est possible, si l'utilisateur a voulu faire le malin, que la pile contient encore plus d'un élément. Il faut donc appeler la fonction pile_clear() avant de renvoyer le résultat. On obtient donc logiquement ceci :
final = pile_pop(&ma_pile);
pile_clear(&ma_pile); // au cas où ...
return final;

Et voilà ! En résumé, on obtient le code complet de la fonction zMath() suivant :
int zMath(const char* calcul, size_t taille)
{
    int i, temp1, temp2, final;
    Pile* ma_pile = NULL;
    i = temp1 = temp2 = final = 0;

    for(; i < taille; i++)
    {
        if(calcul[i] >= '0' && calcul[i] <= '9') // si c'est un chiffre...
            pile_push(&ma_pile,calcul[i]-'0');
        // '-0' permet de "convertir" l'information numérique d'un caractère en "vrai" chiffre
        else if(calcul[i] == '*')
        { // multiplication
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2*temp1);
        }
        else if(calcul[i] == '/')
        { // division
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2/temp1);
        }
        else if(calcul[i] == '+')
        { // addition
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2+temp1);
        }
        else if(calcul[i] == '-')
        { // soustraction
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2-temp1);
        }
        else // caractère invalide
            printf(" Erreur : %c n'est pas considéré comme un caractere valide ! \n", calcul[i]);
    }
    final = pile_pop(&ma_pile);
    pile_clear(&ma_pile); // au cas où ...
    return final;
}


On teste !



Je vous donne donc un main pour tester tout cela ;) :
int main(void)
{
    char saisie[200];
    int i;
    printf("Votre calcul NPI : ");
    fgets(saisie,200,stdin); // saisie

    for(i = 0; i<200; i++)
    {
        if(saisie[i] == '\n') // là où l'utilisateur a appuyé sur entrer...
        { // ...on marque juste la fin de chaîne pour pouvoir utiliser strlen
            saisie[i] = '\0';
            break;
        }
    }

    printf("Résultat : %i", zMath(saisie,strlen(saisie)));
    return 0;
}

Notez que j'utilise strlen() (oui, si l'on marque la fin de la chaîne à évaluer par '\0', strlen est fiable), incluez donc string.h si vous ne voulez pas vous retrouver avec un joli warning. ;) En compilant ce code, on peut rapidement s'apercevoir qu'il fonctionne :
Votre calcul NPI : 13+41+*
Résultat : 20

Bien sûr, on peut facilement améliorer tout cela, par exemple en gérant l'opérateur '^' pour les puissances, ou en vérifiant la validité de l'expression NPI.

Merci à tous les participants !

crys
  • Partager sur Facebook
  • Partager sur Twitter
11 janvier 2009 à 23:23:40

Si sa c'est un "EXERCICES POUR DÉBUTANTS EN C" je croit que mon dico s'est gouré sur la definition du mot debutant ... .
  • Partager sur Facebook
  • Partager sur Twitter
12 janvier 2009 à 0:33:38

En fait, l'exercice était plus simple que tu ne le crois. Mais, tu as raison. D'ailleur, je me suis juré de proposer des exercices plus simples et le prochain sera plus simple.
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2009 à 17:10:19

Dommage que tu n'expliques pas comment gérer les nombres à plus d'un chiffre, mais peut-être tu as eu peur de mettre trop de notions d'algorithme.

Et sinon :
else if(calcul[i] == '*')
        { // multiplication
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2*temp1);
        }
        else if(calcul[i] == '/')
        { // division
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2/temp1);
        }
        else if(calcul[i] == '+')
        { // addition
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2+temp1);
        }
        else if(calcul[i] == '-')
        { // soustraction
            temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);
            pile_push(&ma_pile, temp2-temp1);
        }


C'est moche et redondant ! La preuve, tu as même fait du copier/coller (cf commentaires).
Mieux vaut faire une fonction spécifique pour ceci et surtout mettre :
temp1 = pile_pop(&ma_pile);
            temp2 = pile_pop(&ma_pile);

seulement une fois en début de fonction et pas tout le long du code ...

Tu peux aussi utiliser un switch qui est, je pense, plus approprié ici (après c'est aussi question de goût).

void calcul(Pile **pile, char operateur)
{
    int val1 = 0, val2 = 0;
    val1 = depiler(pile);
    val2 = depiler(pile);

    switch(operateur)
        {
            case '+' :
                empiler(pile, val1+val2);
                break;
            case '-' :
                empiler(pile, val2-val1);
                break;
            case '/' :
                empiler(pile, val2/val1);
                break;
            case '*' :
                empiler(pile, val1*val2);
                break;
            default :
                printf("ERREUR : Mauvaise entree\n");
                break;
        }
}


  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2009 à 19:29:43

J'ai horreur des switch. ;) Pour l'idée de la factorisation du code, tu n'es pas sûr de tomber sur un opérateur, la solution serait alors de créer une deuxième fonction - comme tu l'as fait -, ce qui impliquerait au moins un test non-nécessaire. Je préfère ma méthode et entres-nous, je pense qu'ici, ce détail de répétition est largement négligeable (je ne suis pas un puriste du C). ;) Et sinon, non, je n'ai rien copié-collé.

Citation : May_Hop

Dommage que tu n'expliques pas comment gérer les nombres à plus d'un chiffre, mais peut-être tu as eu peur de mettre trop de notions d'algorithme.


Tout à fait. Déjà qu'on m'en veut de proposer des exercices comme celui-là. ^^ Pour gérer les nombres à plus d'un chiffre, je pense que le mieux est de commencer par lexer la chaîne avant traitement.
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2009 à 19:43:49

Citation : crys'

Et sinon, non, je n'ai rien copié-collé.




Citation : crys'

else if(calcul[i] == '*')
{ // multiplication
    temp1 = pile_pop(&ma_pile);
    temp2 = pile_pop(&ma_pile);
    pile_push(&ma_pile, temp2*temp1);
}


Idem pour la division :

else if(calcul[i] == '/')
{ // multiplication
    temp1 = pile_pop(&ma_pile);
    temp2 = pile_pop(&ma_pile);
    pile_push(&ma_pile, temp2/temp1);
}




A moins que tu es tapé 4 fois de suite de le commentaire multiplication ...

Citation : crys'

tu n'es pas sûr de tomber sur un opérateur



Le default est là pour ça. A moins que tu parles d'autres choses.
default :
                printf("ERREUR : Mauvaise entree\n");
                break;


Suffit de faire quelque chose dans le genre :

int zMath(const char* calcul, size_t taille)
{
    int i, temp1, temp2, final;
    Pile* ma_pile = NULL;
    i = temp1 = temp2 = final = 0;

    for(; i < taille; i++)
    {
        if(calcul[i] >= '0' && calcul[i] <= '9') // si c'est un chiffre...
            pile_push(&ma_pile,calcul[i]-'0');
        // '-0' permet de "convertir" l'information numérique d'un caractère en "vrai" chiffre
        else 
            calcul(blablabla);

    final = pile_pop(&ma_pile);
    pile_clear(&ma_pile); // au cas où ...
    return final;
}
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2009 à 19:49:02

D'accord, mais alors dans le default, il faut rempiler ce que tu as dépilé au début. Moi, je préfère dépiler uniquement quand c'est nécessaire.

Citation : May_Hop

A moins que tu es tapé 4 fois de suite de le commentaire multiplication ...


Bien sûr que non. Quand je disais "je n'ai rien copié-collé", je parlais du copier-coller durant la rédaction du post. Or ce n'est pas le cas, c'est une inattention qui était déjà présente dans le main.c de mon code, à la base donc. J'édite et puis de toute façon, ça n'a pas grande importance.

Je posterai le prochain exercice demain, et, je vous le promet, il sera simple ! ^^
  • Partager sur Facebook
  • Partager sur Twitter
13 janvier 2009 à 21:06:32

Citation : crys'

D'accord, mais alors dans le default, il faut rempiler ce que tu as dépilé au début. Moi, je préfère dépiler uniquement quand c'est nécessaire.



Ah oui. Mais bon en même temps il suffit de rajouter une seule condition. La lisibilité du code serait largement mieux !


Quand tu fais un copier/coller dans un code, car c'est l'origine de cette répétition (sauf si erreur ;) ), il faut se poser des questions et voir si on ne peut pas éviter ces répétitions.
  • Partager sur Facebook
  • Partager sur Twitter