Calculatrice notation scientifique - C - Programmation
Marsh Posté le 17-11-2012 à 21:54:10
Salut
Pour un programme de débutant c'est plus qu'excellent. Bon ta façon de faire n'est pas super claire mais c'est inhérent à tout le monde (on a toujours l'impression que nos commentaires sont super explicites et que ceux des autres ne le sont pas assez) et je suis sûr qu'en s'y attelant une dizaine de minutes on perçoit facilement ta méthode.
Sinon j'ai tapé qq essais
Citation : C:\temp>a.exe |
Et comme tu vois, pour moi zéro souci...
Pour l'optimisation bien entendu faut d'abord avoir compris ton algo. Mais ne compte pas sur les macros pour ça car celles-ci sont intégralement retraduites avant compilation.
Maintenant (après une seconde lecture) petite erreur que j'ai remarqué dans decp(). Tu mets p à 1 si a > 0 sinon tu mets p à 0 puis tu multiplies ce p "a" fois. Là il y a une maladresse car il faut relire ce code 2 ou 3 fois pour comprendre que dans le cas où a vaut 0, la boucle ne sera pas traitée et la valeur renvoyée sera finalement 0.
Une façon d'écrire que je trouve plus claire aurait pu être celle-ci
Code :
|
Ainsi je commence toujours par éliminer les cas particuliers ou erronés avant de m'attaquer au cas général. Je sais que le puriste va hurler devant cette fonction ayant plus d'un return mais ma philosophie est de dire qu'un bon programmeur respecte les règles tandis qu'un programmeur brillant les transgresse parfois.
Sinon, accessoirement, 10^0=1 ...
Marsh Posté le 17-11-2012 à 22:51:36
Merci d'avoir pris le temps de regarder ! pour les tests, j'ai dû imaginer pas mal de cas, ce qui m'a amener à compléter le code au fur et à mesure.. La partie chiante quoi, avec des printf partout pour contrôler ce qui se passe.
Effectivement decp() était pas terrible. Je sais pas trop ce que j'ai foutu, pas besoin de cette condition sur p, tout simplement :
Code :
|
Fonctionne très bien.
J'ai édité mon post, en rajoutant des commentaires qui me semblaient utiles justement autour de l'unique appel à cette fonction, au début de calc() :
Code :
|
Donc, de toute façon decp() ne peut pas recevoir zéro vu qu'on l'appelle avec pour argument (j - decInd) avec la condition decInd != j
Mais c'est plus cohérent d'avoir une fonction qui renvoie 1 pour 10^0, oui !
Sinon, pourquoi c'est mal d'avoir plusieurs return ?
Marsh Posté le 17-11-2012 à 23:17:56
Ah merde, bug détecté. Va falloir ressortir les printf
Je pense que ma méthode est un peu tordue, il doit y avoir des chemins plus simples.
Marsh Posté le 17-11-2012 à 23:26:17
Bonsoir !
Vous devriez passer le paramètre MAXLGHT à la fonction "GetStr", car en l'état, si la personne saisit plus de caractères que prévu, vous obtenez un dépassement mémoire, alors que vous pourriez facilement faire le test dans la fonction GetStr pour régler le problème à la source.
Bon courage pour votre bug !
Marsh Posté le 18-11-2012 à 00:25:25
Salut,
Effectivement, je vais modifier GetStr, merci pour la remarque !
Et j'ai déniché le bug : une simple histoire de variables temporaires pas remises à zéro, demain je posterai le code corrigé
Au fait, y a-t-il un moyen de coller du texte dans ma console, à partir d'une expression copiée dans windows ?
Marsh Posté le 18-11-2012 à 09:54:09
Version corrigée :
Code :
|
Après moult tests sur des calculs bien fournis, je pense que cette version est correcte.
Il faudrait ajouter quelques opérateurs comme racine carrée et fonctions trigo mais pfff.. On verra plus tard
Marsh Posté le 18-11-2012 à 10:15:19
Tout d'abord bravo pour votre apprentissage réussi !
J'émets toutefois quelques doutes sur la capacité à rajouter beaucoup de fonctionnalités, notamment à cause de l'analyse syntaxique faite "à la main". Si vous voulez réellement poursuivre dans cette voie, je ne saurais que vous conseiller de vous tourner vers des outils particulièrement adaptés : lex/yacc (ou flex/bison, en version GNU, j'ai l'habitude d'utiliser la vieille dénomination UNIX), qui permettent de faire de l'analyse syntaxique/grammaticale. La programmation d'une calculatrice est d'ailleurs l'un des premiers exercices pour ces outils.
Mais cela déborde peut-être (sûrement ?) de votre but, qui est de vous mettre au C.
Bon courage en tous cas !
Marsh Posté le 18-11-2012 à 10:47:31
ReplyMarsh Posté le 18-11-2012 à 10:58:42
Farian a écrit : Tout d'abord bravo pour votre apprentissage réussi ! J'émets toutefois quelques doutes sur la capacité à rajouter beaucoup de fonctionnalités, notamment à cause de l'analyse syntaxique faite "à la main". Si vous voulez réellement poursuivre dans cette voie, je ne saurais que vous conseiller de vous tourner vers des outils particulièrement adaptés : lex/yacc (ou flex/bison, en version GNU, j'ai l'habitude d'utiliser la vieille dénomination UNIX), qui permettent de faire de l'analyse syntaxique/grammaticale. La programmation d'une calculatrice est d'ailleurs l'un des premiers exercices pour ces outils. Mais cela déborde peut-être (sûrement ?) de votre but, qui est de vous mettre au C. Bon courage en tous cas ! |
Merci
J'ai regardé un peu la doc sur cet outil, c'est clair que ça doit simplifier vachement.
Mon truc ça reste un exercice, pour appliquer les bases, manipuler les chaînes, les tableaux, un peu de manips sur les pointeurs..
Pour le moment je découvre les possibilités du langage, il faudrait que j'installe une partition linux déjà
Rien qu'avec la librairie standard il y a de quoi faire, après j'aurai probablement envie de développer des applis graphiques, on verra
Je pense faire un bts IRIS l'an prochain en alternance, ça devrait être plus facile de trouver une bonne boîte en ayant déjà des acquis.
Marsh Posté le 18-11-2012 à 11:00:58
Où en est ton IA ?
Marsh Posté le 18-11-2012 à 11:11:22
simius_computus a écrit : |
J'écris un compositeur pour instrument MIDI actuellement.
Celui ci exploite les réseaux de neurones.
Si non, il y a jovalise.net, toujours au même point.
Marsh Posté le 18-11-2012 à 11:43:44
le jovalise.net n'a pas changé effectivement
Citation : |
Je suis curieux de savoir comment tu procèdes, avoue, c'est juste du random sur une banque de mots ?
Appliqué à la composition de musique ça peut donner des trucs intéressants remarque
Marsh Posté le 18-11-2012 à 11:46:12
Du tout. ça passe réellement par un réseau de neurone.
L'aléatoire en musique... C'est pas la panacée.
Marsh Posté le 18-11-2012 à 11:48:57
Mais comment ton machin apprend-il ?
On ne peut pas lui signaler ses incohérences.
Marsh Posté le 18-11-2012 à 11:56:02
simius_computus a écrit : Mais comment ton machin apprend-il ? |
Il a d'abord appris un ficher d'exemple. puis au bout d'un certain nombre de requête il repart en apprentissage avec les requête et les réponse qu'il a donné.
Marsh Posté le 18-11-2012 à 12:32:38
Je ne connais rien en réseaux de neurones, mais ça m'intéresse. Tout ce qui est IA, apprentissage
(pour la théorie musicale, par contre je connais )
Marsh Posté le 18-11-2012 à 14:12:45
simius_computus a écrit : pour les tests, j'ai dû imaginer pas mal de cas, ce qui m'a amener à compléter le code au fur et à mesure.. |
Aïe, ça c'est embêtant. Parce qu'en procédant ainsi, tu risques de rater certains cas...
simius_computus a écrit : Ah merde, bug détecté. Va falloir ressortir les printf |
Et voilà
simius_computus a écrit : Je pense que ma méthode est un peu tordue, il doit y avoir des chemins plus simples. |
Peut-être. Par exemple qu'est-ce qu'une expression ? C'est
1: nombre opérande nombre => 2 + 2
Ou bien
2: nombre opérande "définition 1" => 2 + 3 + 4 => 2 + 3 + 4 + 5 ...
Ou bien
3: ( "définition 2" ) => (2 + 3 + 4 + 5)
En utilisant la définition 3, qui elle-même se base sur la définition 2 qui est basée sur la définition 1, tu couvres tous les cas possibles. Imagine donc une fonction ayant pour but de calculer le résultat d'une expression définie en 2 (que des nombres et des opérandes) et qui, dès qu'elle détecte une parenthèse, se met en attente du résultat de ce qu'il y a entre parenthèses avant de continuer. Et ce résultat pourrait être donné par un clone de cette même fonction mais exécuté dans un sous-contexte.
Ben toute cette manipulation est rendue possible par l'utilisation astucieuse de la récursivité. Avec en amont de tout ça une première fonction dédiée à vérifier la syntaxe générale (surtout au niveau de la parité des parenthèses) et là tu es certain que ton code couvre toutes les combinaisons...
Et, accessoirement, il a été cité dans ce topic lex et yacc, outils justement dédiés à ce genre de problème. Tu décris dans un fichier texte comment tu définis une expression (comme je l'ai fait au dessus) et yacc génère automatiquement le code C correspondant pour traiter une expression. Et lex te permet de programmer le vérificateur de syntaxe. Malheureusement ces outil sont issus du monde Unix et je ne sais pas s'ils existent pour zindow. Mais bon, rien n'interdit de programmer tout ça manuellement surtout si c'est pour se faire un bon exercice...
Marsh Posté le 18-11-2012 à 14:59:38
La récursion en partant du bas (1er niveau dans les parenthèses), ouais.. Moi j'ai procédé à l'inverse :
1- chercher la ou les expression(s) appartenant au dernier niveau;
2- calculer le(s) résultat(s) et le(s) placer à gauche dans l'expression correspondante, les autres éléments de l'expression étant marqués comme à ignorer pour la suite;
3- décrémenter la valeur "dernier niveau" et retourner à l'étape 1, ou terminer si on a parcouru tous les niveaux
A mon avis ce système est gourmand avec des boucles qui parcourent l'ensemble de la chaîne, à voir si la récursion offre un avantage, en tout cas j'ai pas dû prendre les options les plus efficaces c'est clair.
C'est dans les détails du calcul de chaque expression que j'ai eu des problèmes de cas particuliers à détecter, pour le dernier bug c'est une étourderie qui avait la mauvaise idée de se compenser d'elle-même dans la majorité des cas
Je compte pas m'éterniser sous windows
Je vais essayer d'autres trucs, et reviendrai sur ce code probablement plus tard pour trouver plus élégant. Merci pour les infos
Marsh Posté le 22-11-2012 à 11:14:53
Lex/yacc, bonne idée. Si c'est dans un but d'apprentissage, cet article sur la grammaire LL devrait t'aider : http://fr.wikipedia.org/wiki/Analyse_LL
Un codage à base d'arbre serait pas mal : tu décomposes ton expression en feuilles (nombres) et les noeuds sont les opérateurs.
J'ai un souci avec ton for( ; ; ) qui pourrait être remplacé de manière plus lisible (à mon avis) par un while. D'un point de vue purement algorithmique, c'est plus logique, donc probablement plus lisible
mais c'est du très bon niveau pour qq'un qui pratique le C depuis qq mois seulement
Marsh Posté le 22-11-2012 à 11:36:22
Simius,
Pour quelqu'un qui a démarré le C en Septembre, ce que tu as fait est très bien.
Ne t'inquiètes pas du commentaires 'c'est mieux avec lex/yacc (ou flex/bison plutôt)' ou de celui critiquant ton 'ce qui m'a amener à compléter le code au fur et à mesure', quand on a démarré la programmation, on a tous procédé ainsi, les manières pour mieux faire, on les apprends avec l'expérience (qui permet de comprendre pourquoi une méthode est meilleure qu'une autre).
Une petite remarque: quand tu veux auto-incrémenter une valeur, pré-incrémentes la: ++i
N'utilises la post-incrémentation (i++) que quand ça a un sens dans le programme de d'abord évaluer la valeur puis de l'incrémenter (j = i++ etc).
C'est une bonne habitude à prendre dès le début (et autrefois, de plus, sur certaines architectures, la pré-incrémentation était plus rapide).
Bon, je vais lire le code un peu plus en détail...
A+,
Marsh Posté le 22-11-2012 à 12:47:01
Bon, alors ce qui ne me plait pas dans ce code:
- Il n'est pas prévu de sortir du programme autrement que avec Ctrl+C apparemment. Le faire sur une ligne entrée vide me semblerait logique.
- int getStr(char c[]) Tu passes une variable cachée à cette fonction, MAXLGHT.
définis la plutôt comme int getStr(char c[], int maxlen) et appelles la avec i = getStr(read, MAXLGHT);
Note: une autre définition possible pourrait être int getStr(char (*c)[MAXLGHT]); avec un appel i = getStr(&read); mais c'est un niveau un poil plus avancé, ou on dit explicitement que le paramètre est l'adresse d'un tableau de MAXLGHT chars.
Bon, je vais manger.
Bon, il y a sans doute des choses à améliorer dans l'algo, car en traçant un peu:
/* * */ printf("left = %f right = %f type = %c ", calcFormat[left], calcFormat[right], type[left]);
result = lvlCalc(&calcFormat[left], &calcFormat[right], &type[left], &zdiv); //calcule le niveau en cours
/* * */ printf("result = %f\n", result);
je vois ceci, avec un appel inutile manifestement:
1+(2.1*3)-2
left = 2.100000 right = 3.000000 type = d result = 6.300000
left = 0.000000 right = 0.000000 type = ¶ result = 0.000000
left = 1.000000 right = 2.000000 type = d result = 5.300000
5.300000
il pourrait peut être aussi être moins strict sur ce qui passe en entrée:
+2
erreur de syntaxe
0.000000
3 * -1
erreur de syntaxe
0.000000
A+,
Marsh Posté le 22-11-2012 à 17:45:19
Merci pour votre expertise
rufo a écrit : Lex/yacc, bonne idée. Si c'est dans un but d'apprentissage, cet article sur la grammaire LL devrait t'aider : http://fr.wikipedia.org/wiki/Analyse_LL |
Quel for( ; ; ) ? J'en ai mis deux, conscient que c'était une solution pas forcément élégante
Dans main() je pourrais le remplacer par un truc qui met fin au programme comme l'a suggéré Gilou.
Pour la grammaire, je regarde ça, intéressant mais c'est quand même assez abstrait
(wikipédia ne m'a jamais semblé didactique)
Je note pour la place de l'incrément, c'est en effet logique.
En quoi l'utilisation de MAXLGHT est-elle gênante dans getStr ? ("variable cachée" ?)
Alors pour l'erreur dans ton traçage, je crois avoir remarqué ce truc avant mais j'ai oublié de chercher une correction vu que ça n'empêchait pas le fonctionnement.
Un problème de référence à des éléments de tableau pas encore définis je pense. Vais essayer d'arranger ça !
Marsh Posté le 22-11-2012 à 18:22:07
gilou a écrit : |
Bon, c'est corrigé, quelques conditions en plus dans la boucle qui appelle lvlCalc() :
Code :
|
Marsh Posté le 22-11-2012 à 22:05:15
Citation : |
La variable est cachée dans le sens où vous déclarez votre tableau avec cette taille-là, mais vous ne l'utilisez pas dans la fonction getStr, puis vous comparez le résultat à cette valeur en sortie de la fonction.
Si l'utilisateur rentre une "phrase" trop longue, vous aurez tout d'abord un écrasement mémoire dans la fonction getStr à cause du dépassement du nombre de valeurs allouées, puis, si le dépassement n'a pas tout déstabilisé, vous affichez une erreur en disant que le nombre maximum de caractères autorisés est dépassé, mais le mal est (peut-être) déjà fait.
Marsh Posté le 22-11-2012 à 23:10:54
Pourtant j'utilise MAXLGHT dans getStr()
Code :
|
En toute logique, si l'utilisateur dépasse MAXLGHT caractères en entrée, le tableau passé en argument ne sera plus rempli, seul i sera incrémenté non ?
Je pense qu'il y a un truc que je ne saisis pas..
Marsh Posté le 22-11-2012 à 23:58:08
En effet, j'avais mal vu ! La façon de faire n'est pas la plus élégante (pour que la fonction soit plus facilement réutilisable, il faudrait passer la taille à laquelle la chaîne a été allouée en paramètre à la fonction getStr).
Marsh Posté le 22-11-2012 à 23:59:38
Justement, tu l'utilises alors que c'est une variable externe à ta fonction. Ta variable est cachée: c'est un paramètre de ta fonction (modifier sa valeur modifie le comportement de ta fonction), mais elle n'est pas déclarée dans la liste des paramètres de ta fonction.
C'est une mauvaise habitude de ne pas expliciter tous les paramètres d'une fonction. Sur un petit programme comme cela, ça semble anodin, mais avoir ce genre de pratique finit toujours par jouer des tours.
Tu pouvais soit déclarer
Code :
|
et faire un appel i = getStr(&read);
C'est complexe, mais c'est la manière dont il faut déclarer le paramètre afin que sizeof retourne la taille effective du tableau.
ou bien tu peux faire avec le paramètre explicite:
Code :
|
et faire un appel i = getStr(read, MAXLGHT);
A+,
Marsh Posté le 23-11-2012 à 00:28:35
Et en remplacement de ton gerStr, pour lire l'input utilisateur, tu peux sauvegarder cette fonction 'la plus blindée possible' dans ta bibliothèque de code perso:
Code :
|
que tu appelles avec
char buffer[BUFSIZE];
.....
read_input(buffer, sizeof buffer);
et qui copiera dans buffer au plus les BUFSIZE-1 premiers caractères entrés par l'utilisateur, ajoute un \0 en dernière position si nécessaire, et nettoiera les caractères restant dans le buffer d'entrée. La valeur retournée est le nombre de caractères copiés dans buffer.
Note: la variante suivante
Code :
|
fait la même chose (au cas n = 0 près, qui n'a guère de sens), mais a pour valeur de retour le nombre de caractères tapés par l'utilisateur (ce qui permet de savoir s'il a dépassé la capacité autorisée).
A+,
Marsh Posté le 23-11-2012 à 10:02:56
D'accord, j'ai compris. Merci
Faudra que je lise la doc sur la bibli standard, pour le moment je suis jamais sorti des printf et getchar
Marsh Posté le 17-11-2012 à 19:32:43
Salut
J'ai commencé le C en septembre, ça me faisait envie depuis longtemps mais j'avais la flemme de sortir de ce que je bidouillais en html/js ( ). Evidemment c'est un autre univers, pour le moment je me limite à des trucs sur console, déjà il y a de quoi faire. Après avoir lu une bonne partie du Kernighan&Ritchie, plus le siteduzero, voici mon premier programme utile qui permet donc d'analyser et calculer la saisie de l'utilisateur en mode scientifique, avec les priorités d'opérateurs, les niveaux de parenthèses, la détection des erreurs de syntaxe et d'éventuelles divisions par zéro. Vlà le code :
Alors déjà, si vous voulez le tester dans tous les sens, j'ai peut-être négligé des choses.
Et puis au niveau du code il y a sûrement moyen d'optimiser, tant en volume de code source (utiliser des macros ?) qu'en performances dans l'exécution, je suis preneur de vos conseils. J'ai essayé de mettre des commentaires qui rendent la chose plus facile à examiner
Merci,
Message édité par simius_computus le 17-11-2012 à 22:40:48
---------------
IWH --- Le forum de toute une génération : http://losersiv.1fr1.net (losers, sans-ami, dépressifs, allez on va faire cette merde)