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);
}
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 :
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.
@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
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.
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)
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...
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.
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).
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
*/
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 ).
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);
}
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
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 !