Partage
  • Partager sur Facebook
  • Partager sur Twitter

Exercices pour débutants en C

Au menu : zSommeChiffres (nombres, algo)

13 juin 2009 à 17:12:59

Citation : Kushou

Bonjour,

Bon ben voilà une petite participation, mais je suis plutot mauvais en C (vous avez dit partout ?), donc mon code est surement moche. Pour pas faire une boucle comme tout le monde, et histoire qu'on ait de quoi discuter, j'ai décidé de faire ça en récurcif (tail-rec en plus %%). Donc le code est ici. On peut choisir les délimiteurs entre (), [] et {}, voilà. Le code est un peu plus long que celui des autres. Si j'avais réussi a faire fonctionner fflush, il auraut fait 42 lignes, c'est à souligner.


J'avais aussi essayé une version récursive(voir un peu plus haut), mais ta fonction est bien plus jolie que la mienne :-°
Ta fonction passe les quelques tests que j'avais préparé...
J'ai retouché ma fonction zBrassRec en te plagiant...
static char zBrassRec(char const *src, char open, char close, int lvl)
{
    /* Si le niveau d'imbrication est négatif, le parenthèsage est incorrect. */
    if (lvl < 0)
        return -1;
    /* Si on est à la fin de la chaîne et que le niveau d'imbrication courant
     * est nul, la chaîne est correctement parenthèsée.
     */
    if (*src == '\0')
        return !lvl ? 0 : -1;

    if (*src == open)
        lvl++;
    else if (*src == close)
        lvl--;

    return  zBrassRec(src + 1, open, close, lvl);
}

Merci...
  • Partager sur Facebook
  • Partager sur Twitter
Zeste de Savoir, le site qui en a dans le citron !
13 juin 2009 à 17:52:32

Bonjour à tous !

Premièrement, merci à tous les participants (et futurs participants) ! Je vois que les codes postés fonctionnent tous sous le même principe et que ce qui diffère n'est pas l'algo. L'implémentation récursive de Kushou est également pas mal, pour changer un peu. Ce que je trouve un peu dommage (mais bon, c'est sûrement normal), c'est que personne n'a proposé d'autre algorithme.

Bah je vais m'y mettre. Ici, je propose une implémentation de l'algorithme d'analyse syntaxique descendante. C'est pertinent et approprié car on constate qu'ici, on souhaite différencier une expression syntaxiquement bonne d'une expression syntaxiquement fausse. On parle bien de syntaxe, syntaxe que l'on peut vérifier avec des algorithmes d'analyse syntaxique (ici par descente récursive). La syntaxe des expressions correctes peut facilement se définir (en suivant notre énoncé) avec la grammaire non-contextuelle LL(1) suivante :

pth -> '(' pth ')' pth
     | c pth       // c représente tout caractère différent de '(' et de ')'
     | RIEN


On peut ici se passer d'une analyse lexicale et appliquer l'analyse syntaxique directement sur la chaîne d'entrée. Par contre, on ne construira pas d'arbre syntaxique parce que ce n'est pas cela qui nous intéresse ici : on veut juste savoir si l'analyseur syntaxique valide l'expression. La technique est assez simple : l'analyseur syntaxique itère sur la chaîne en "avalant" (ce qui revient à incrémenter l'itérateur) les lexèmes reconnus comme syntaxiquement corrects. Nous n'allons pas lancer d'erreurs dans les cas d'erreurs mais nous allons simplement laisser l'analyse syntaxique se dérouler puis vérifier à la fin uniquement s'il reste des caractères non-reconnus (donc si l'itérateur (qui sera de type unsigned int) est différent de la taille de l'expression).

Je crois qu'un code vaut mieux qu'un long discours :
// 1 : bien
// 0 : pas bien

/** pth -> ( pth ) pth | l pth | RIEN **/

unsigned int* incr(unsigned int* i)
{
    (*i)++; return i;
}

int pth(const char* str, unsigned int* i, unsigned int size)
{
    if((*i) >= size) {
        return 1;
    }
    if(   (str[*i] == '\0')
       || (str[*i] == '(' && pth(str, incr(i), size) && str[*i] == ')' && pth(str, incr(i), size))
       || (str[*i] != ')' && pth(str, incr(i), size))  )
        return 1;
    return 1;
}

int verif(const char* str)
{
    unsigned int i = 0;
    pth(str, &i);
    if(i != strlen(str))
        return 0;
    return 1;
}
  • Partager sur Facebook
  • Partager sur Twitter
13 juin 2009 à 20:33:53

Citation : ShareMan

int pth(const char* str, unsigned int* i)
{
    if(   (str[*i] == '\0')
       || (str[*i] == '(' && pth(str, incr(i)) && str[*i] == ')' && pth(str, incr(i)))
       || (str[*i] != ')' && pth(str, incr(i)))  )
        return 1;
    return 1;
}

Tu n'aurais pas voulu dire return 0 ? :o
Là la fonction retourne 1 dans tous les cas.
  • Partager sur Facebook
  • Partager sur Twitter
13 juin 2009 à 22:31:38

De toute façon la valeur de retour n'est pas récupérée lors de l' appel à la fonction dans la fonction verif().
  • Partager sur Facebook
  • Partager sur Twitter
14 juin 2009 à 0:19:58

Justement, la valeur de retour ne nous intéresse pas ici. J'ai juste mis un petit truc afin de pouvoir intégrer les appels récursifs directement dans le if (je cherche dans le concis). Il faut bien comprendre ce qui est utile : les incrémentations de l'itérateur. C'est cela qui compte et c'est sur cela qu'on va se baser à la fin pour savoir si toutes les unités lexicales ont été "avalées" (dans ce cas, l'expression est syntaxiquement correcte). Comme dit, la valeur de retour n'est vraiment pas importante ici, elle n'a aucun rôle premier. Si vous voulez, je peux poster une version sans, mais forcément, ça rend moins bien.
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
22 juin 2009 à 1:44:20

Personnellement, j'ai l'impression que la solution pour zReader est lourde, aussi je propose la mienne :

#include <stdio.h>
#include <stdlib.h> /*exit*/

void fromTo(FILE* in, FILE* out) {
    char c;
    for(; (c = fgetc(in)) != EOF; fputc(c, out));
}

void lire(char* s) {
    FILE* f = (*s == '-') ? stdin : fopen(s, "r");
    if(f != NULL) {
        fromTo(f, stdout);
        fclose(f);
    } else {
        perror("E ");
        exit(1);
    }
}

void editer(char* s, char edit) {
    char mode[3] = "a+";
    FILE* f = fopen(s, mode);
    if(f == NULL) {
        perror("E ");
        exit(1);
    }
    if(edit != 'c')
        fromTo(stdin, f);
    fclose(f);
}

int format(char* name) {
    return fprintf(stderr, "Usage: %s -[r|e|c] FILE\n", name);
}

int main(int argc, char* argv[]) {
    if(argc < 3) {
        format(*argv);
        return 1;
    }
    switch(argv[1][1]) {
    case 'r': lire(argv[2]); break;
    case 'e': case 'c':
        editer(argv[2], argv[1][1]); break;
    default: format(*argv); return 1;
    }
    return 0;
}


En 2 fois moins de lignes, sans utiliser de tableau de caractères pour l'entrée !

Utilisation : -e pour éditer, -c pour créer, -r pour lire.
Envoyez ^D (ou ^Z sur Windows ?) pour terminer la saisie.

Pour les linuxiens (et autres qui savent utiliser la console) c'est bien plus pratique :

% echo 'Et je rajoute une phrase' | ./p -e wat
% ./p -r wat
Et je rajoute une phrase
%
  • Partager sur Facebook
  • Partager sur Twitter
26 juin 2009 à 15:45:45

@ShareMan : Tu penses que les zéros comprennent ce que veut dire une grammaire, ou encore grammaire non-contextuelle, ou pire : grammaire LL(1) dans notre cas... ?!
S'ils comprennent ça c'est qu'ils ne sont pas des zéros
  • Partager sur Facebook
  • Partager sur Twitter
26 juin 2009 à 16:04:08

Pour comprendre les techniques employées dans cette implémentation, il n'est pas nécessaire de savoir ce qu'est une grammaire (même si hum, c'est évident, c'est un terme très courant en France), et pour le "non-contextuelle", il ne faut pas aller chercher bien loin. Ensuite, je n'ai parlé à aucun moment de grammaire LL(1), et même pas de grammaire prédictive, ça n'a pas sa place sur ce topic qui n'est pas un cours sur les techniques d'analyse syntaxique.

Les gens motivés pour découvrir la méthode que je propose, appropriée ici, qui diffère de l'autre n'ont pas à chercher loin pour savoir ce que représente les fameuses "grammaires non-contextuelles" ou "analyse syntaxique descendante". Les gens qui ne s'y intéressent pas ne liront même pas.
  • Partager sur Facebook
  • Partager sur Twitter
26 juin 2009 à 16:15:23

Justement, j'ai fait la remarque car les gens ne comprendront pas pourquoi tu utilises un algo qui peut paraitre à premiere vue trop compliqué pour rien.

Citation : ShareMan

(même si hum, c'est évident, c'est un terme très courant en France)


:lol:

Citation : ShareMan

je n'ai parlé à aucun moment de grammaire LL(1)



Je sais, mais la grammaire que tu proposes en est une.


Mais bon, désolé si ma remarque ne t'a pas plu, j'aurai peut être du m'abstenir...
  • Partager sur Facebook
  • Partager sur Twitter
26 juin 2009 à 16:28:01

Bah "à première vue" peut-être mais ça n'a rien de "compliqué pour rien". La technique en elle est simple, c'est comprendre le code sans explications et bases solides derrières qui l'est moins. Dans notre cas, j'ai essayé d'expliquer le minimum du minimum, en utilisant le minimum de termes "techniques".

La grammaire que je présente est effectivement LL(1), puisqu'il suffit d'observer le premier élément de gauche pour choisir la bonne production. Mais si tu te plains déjà du terme "grammaire", penses-tu vraiment qu'il serait nécessaire de préciser le caractère LL(1) ?

Et hum, je n'ai rien contre tes remarques, je ne suis jamais contre les retours d'avis. :)
  • Partager sur Facebook
  • Partager sur Twitter
26 juin 2009 à 16:39:50

Citation : ShareMan

Et hum, je n'ai rien contre tes remarques, je ne suis jamais contre les retours d'avis. :)


Super alors ! je vais continuer dans ce cas...

Citation : ShareMan

penses-tu vraiment qu'il serait nécessaire de préciser le caractère LL(1) ?


Oui car c'est grâce à cette propriété qu'il suffit de regarder un seul caractère pour construire le mot.

Citation : ShareMan

Bah "à première vue" peut-être mais ça n'a rien de "compliqué pour rien".


Quand un débutant fait un programme avec un compteur pour compter le nombre de parenthèses ouvrantes, et le comparer avec le nombre de parenthèses fermantes, il va se demander pourquoi tu as fait quelque chose de très très différent.

Moi le premier quand j'ai étudié la compilation dans le cours : Théorie des langages, j'ai trouvé ça un peu bizarre au début, car on utilisait des canons pour tuer des mouches, mais après ça c'est avéré très utile...

Donc voila, mon message aux zéros c'est : L'algo que ShareMan a utilisé découle d'une théorie, avec ses règles et tout... (c'est toujours bon de savoir ce genre de trucs).
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
29 juin 2009 à 19:52:29

Bon, je m'ennuyais alors j'ai fait le sujet, j'ai limiter la chaine a 20 mais bon peu importe:

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

int main(void)
{
    int i=0, somme=0; // i pour la boucle, somme pour les parenthèses
    char chaine[20]; // chaine entrée

    printf ("? ");
    scanf ("%s", chaine);

    while (chaine[i] != '\0') // TQ on n'est pas à la fin
    {
        if (chaine[i] == '(')
            somme++;

        else if (chaine[i] == ')')
            somme--;

        if (somme < 0)
        {
            printf ("Mal parenthésé !"); // si la somme est négative, c'est mal parenthésée !
            return 0;
        }

        i++;
    }

    if (somme != 0) // si a la fin de la chaine, somme != 0, c'est mal parenthésée !
        printf ("Mal parenthésé !");

    else
        printf ("Bien parenthésé !");

    return 0;
}

/* très simple ce code, une expression est correctement parenthésée si :   
 - Autant de parenthèse ouvrantes que fermantes
 - Jamais plus de fermantes que d'ouvrantes à une position 'i' de la chaine
*/



  • Partager sur Facebook
  • Partager sur Twitter
29 juin 2009 à 21:35:06

Voici ma "trés" modeste contribution :

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

static void purger(void);
static void clean (char *chaine);
void check(char *phrase);

int main(void)
{
    char phrase[512] = "";

    printf("zBrace - Human-Behind version.\n\n");
    printf("Entre une phrase (avec ou sans parenthese) :\n");

    fgets(phrase, sizeof(phrase), stdin);
    clean(phrase);

    check(phrase);
    
    return 0;
}

void purger(void)
{
    int c;

    while ((c = getchar()) != '\n' && c != EOF)
    {}
}

void clean (char *chaine)
{
    char *p = strchr(chaine, '\n');
    int c;

    if (p)
    {
        *p = 0;
    }
    else
    {
        purger();
    }
}

void check(char *phrase)
{
    int i = 0;
    int pLeft = 0;
    int pRight = 0;

    for(i = 0; i < strlen(phrase); i++)
    {
        if(phrase[i] == '(') pLeft++;
	else if(phrase[i]== ')') pRight++;
    }

    if(pLeft == pRight) printf("Les parentheses sont bien toutes fermees.\n");
    else if(pLeft > pRight) printf("Il manque %d parenthese(s) fermante(s).\n", pLeft-pRight);
    else if(pLeft < pRight) printf("Il manque %d parenthese(s) ouvrante(s).\n", pRight-pLeft);
}

  • Partager sur Facebook
  • Partager sur Twitter
29 juin 2009 à 21:41:23

Citation : Human-Behind

Voici ma "trés" modeste contribution :


teste cette phrase :
) coucou (
  • Partager sur Facebook
  • Partager sur Twitter
29 juin 2009 à 21:58:51

+1 Arthurus , je n'avais pas pensé à ce cas :p .
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
29 juin 2009 à 23:22:08

Quand saurons nous si notre code est correct ou pas ?
  • Partager sur Facebook
  • Partager sur Twitter
30 juin 2009 à 0:04:28

Le meilleur (seul ?) moyen de savoir si un code est correct, c'est de prouver qu'il fonctionne toujours. La première chose à faire après l'avoir écrit est bien entendu de la passer à toute une batterie de tests, incluant l'essai classique que propose Arthurus. Je suppose que tu parlais d'une éventuelle correction Ver2terre, et oui, il y a pour chaque exercice une correction organisée trois ou quatre semaines après la proposition (prévoyez un retard :-° ).
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
30 juin 2009 à 1:50:58

mon code marche car il est logique :-°:-°

(voir mon code...)
  • Partager sur Facebook
  • Partager sur Twitter
30 juin 2009 à 1:58:28

LOL, si on pouvait dire "mon code est logique" et en rester là... je n'aurais eu que des 20 à mes partiels...
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
30 juin 2009 à 2:14:30

Citation : Crehn

Bon voici ma version claire et propre:

#include <stdio.h>

int cb(char *s){int n=0;for(;*s;++s){if(*s==
'(')++n;if(*s==')')if(--n<0)break;}return n;}

int main(int argc, char **argv) {
    if(cb(argv[1])) {
        puts("Ok.");
    } else {
        puts("Damn.");
    }

    return 0;
}



EDIT:
Hop, et encore plus petit! Fonction de 90 caractères.



Bravo, tu obtient le prix du code le moins lisible du monde
  • Partager sur Facebook
  • Partager sur Twitter
30 juin 2009 à 2:20:02

Un code petit n'implique pas un code performant.
  • Partager sur Facebook
  • Partager sur Twitter
2 juillet 2009 à 19:09:00

Je n'ai rien compris au code de Shareman, ptet que demain ça ira mieux. :-/
Voici un truc simple, sans lecture depuis un fichier parce que je suis un pouilleux.
#include <stdio.h>
#include <stdlib.h>

int isValid(const char* str);

int main(int argc, char** argv)
{
    const char* str = argv[1];
    if (isValid(str))
        printf("Statement %s is valid.\n", str);
    else
        printf("Statement %s is not valid.\n", str);

}

int isValid(const char* str) {
    int i = 0, o = 0, c = 0;
    
    for (i = 0 ; str[i] != '\0' ; i++) {
        switch(str[i]) {
            case '(': o++; break;
            case ')': c++; break;
            default: break;
        }

        if (c > o)
            return 0;
    }
    
    return (c == o);
}
  • Partager sur Facebook
  • Partager sur Twitter
3 juillet 2009 à 12:14:53

Pour en revenir au petits code, c'est parfois impressionnant ce qui peut être fait en si peu de ligne...
Essayez de compiler ca :
main(k){float i,j,r,x,y=-16;while(puts(""),y++<15)for(x
=0;x++<84;putchar(" .:-;!/>)|&IH%*#"[k&15]))for(i=k=r=0;
j=r*r-i*i-2+x/25,i=2*r*i+y/10,j*j+i*i<11&&k++<111;r=j);}


Ce code vient de http://mrl.nyu.edu/~perlin/


RAV avec le sujet d'orgine mais bon : )
  • Partager sur Facebook
  • Partager sur Twitter
Découvrez un petit jeu Android bien sympa : http://www.siteduzero.com/forum/sujet/appli-jeu-android-cube-racer
3 juillet 2009 à 12:25:57

C'est une fractale non ? (ensemble de Mandelbrot)
  • Partager sur Facebook
  • Partager sur Twitter
3 juillet 2009 à 12:34:03

On dirait bien. Mais si vous voulez parler de cela, je vous demande d'ouvrir un nouveau topic.
  • Partager sur Facebook
  • Partager sur Twitter
3 juillet 2009 à 16:44:32

Bon, j'ai essayé l'exercice, mais étant vraiment débutant...J'arrive pas à la cheville des codes déjà postés.

J'espère au moins que le code fait ce qu'on lui demande.

main.c

#include "Zbrace.h"

int
main (void)
{
  char chaine[] = "(j(h))";
  if (Zbrace (chaine))
    {
      printf ("Les parenthèses sont mises correctement\n\n");
    }
  else
    {
      printf ("Parenthèses mal placées\n\n");
    }
  return 0;
}


Zbrace.c
#include "Zbrace.h"

int
Zbrace (char *chaine)
{
  int longueurChaine = my_len (chaine); //longueur de la chaine
  int inter = 0;
  int exter = 0;
  int test = 0;
  for (int i = 0; i < longueurChaine; i++)
    {
      if ((chaine[i] == ')') && (inter == 0)) //si la première parenthèse est ')', retourne faux
	{
	  return test;
	}
      if (chaine[i] == '(') //On regarde si il y a autant de ')' que de '('
	{
	  inter++;
	}
      if (chaine[i] == ')')
	{
	  exter++;
	}

    }
  if (inter == exter) 
    {
      test = 1;
    }
  else
    {
      test = 0;
    }
  inter = exter = 0;
  for (int i = longueurChaine; i != 0; i--)
    {
      if ((chaine[i] == '(') && (exter == 0)) //si la dernière parenthèse est '(', on retourne faux
	{
	  return 0;
	}
      if (chaine[i] == '(')
	{
	  inter++;
	}
      if (chaine[i] == ')')
	{
	  exter++;
	}

    }
  return test;

}

int
my_len (char *chaine)
{
  char cara = 1;
  int longueur = 0;
  while (cara != '\0')
    {
      cara = chaine[longueur];
      longueur++;
    }
  longueur--;
  return longueur;
}


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

int Zbrace(char *chaine);
int my_len(char *chaine);



Je crois que j'ai fais l'algorithme le plus pourris...Deux boucles for, ça fait beaucoup :-°
  • Partager sur Facebook
  • Partager sur Twitter
6 juillet 2009 à 0:29:36

Citation : undefined

Je n'ai rien compris au code de Shareman, ptet que demain ça ira mieux. :-/


Faut pas t'inquieter tu n'est pas le seul, moi même j'ai pas eu l'envie de le decortiquer surtout que l'explication est claire :

Citation : shareman

On peut ici se passer d'une analyse lexicale et appliquer l'analyse syntaxique directement sur la chaîne d'entrée. Par contre, on ne construira pas d'arbre syntaxique parce que ce n'est pas cela qui nous intéresse ici : on veut juste savoir si l'analyseur syntaxique valide l'expression. La technique est assez simple : l'analyseur syntaxique itère sur la chaîne en "avalant" (ce qui revient à incrémenter l'itérateur) les lexèmes reconnus comme syntaxiquement corrects. Nous n'allons pas lancer d'erreurs dans les cas d'erreurs mais nous allons simplement laisser l'analyse syntaxique se dérouler puis vérifier à la fin uniquement s'il reste des caractères non-reconnus (donc si l'itérateur (qui sera de type unsigned int) est différent de la taille de l'expression).


pour ma part je me suis trompé de cible dans ce forum. Il est domage que l'on propose des exercices :
- non gradués dans la complexité
- que les corrections ne soient pas adaptées au public concerné (zero on part de zero : on oublie !)
- de proposer un code sans donner l'ombre d'une démarche constructive, comme un algorithme

alors oui ce soir je me défoule (avant de m'auto-banir de ce forum), et je pense à tous les zeros qui veulent progresser, je suis pas sur qu'ils y trouvent leur compte !
  • Partager sur Facebook
  • Partager sur Twitter
6 juillet 2009 à 1:06:30

Citation : Camp0us

C'est une fractale non ? (ensemble de Mandelbrot)



Vraiment désolé, mais je n'ai pas pu m'empêcher de repondre que ce n'est pas l'ensemble de Mandelbrot.

Encore désolé pour le HS :honte:
  • Partager sur Facebook
  • Partager sur Twitter
6 juillet 2009 à 11:15:50

Pour le post de zx-spectrum :

Je ne vais même pas argumenter, ça ne servirait à rien. cf ici.
  • Partager sur Facebook
  • Partager sur Twitter
6 juillet 2009 à 14:31:47

Citation : ShareMan

Pour le post de zx-spectrum :

Je ne vais même pas argumenter, ça ne servirait à rien. cf ici.



C'est vrai qu'avec ce lien on comprend mieux sa réaction.
  • Partager sur Facebook
  • Partager sur Twitter