Quelle est la façon la plus intelligente de concevoir un analyseur mathématique? Ce que je veux dire, c'est une fonction qui prend une chaîne mathématique (comme: "2 + 3/2 + (2 * 5)") et renvoie la valeur calculée? J'en ai écrit un en VB6 il y a longtemps, mais cela a fini par être trop gonflé et pas très portable (ou intelligent d'ailleurs ...). Des idées générales, du code pseudo ou du vrai code sont appréciés.
Une assez bonne approche impliquerait deux étapes. La première étape implique la notation conversion de l'expression d'infixe en suffixe (par exemple via triage de Dijkstra ). Une fois cela fait, il est assez trivial d'écrire un évaluateur postfix .
J'ai écrit quelques articles de blog sur la conception d'un analyseur mathématique. Il y a un général introduction , des connaissances de base sur grammaires , exemple d'implémentation écrit en Ruby et un suite de tests . Peut-être trouverez-vous ces documents utiles.
Vous avez deux ou trois approches. Vous pouvez générer du code dynamique et l'exécuter afin d'obtenir la réponse sans avoir besoin d'écrire beaucoup de code. Effectuez simplement une recherche sur le code généré lors de l'exécution dans .NET et il existe de nombreux exemples.
Vous pouvez également créer un analyseur réel et générer un petit arbre d'analyse qui est ensuite utilisé pour évaluer l'expression. Encore une fois, c'est assez simple pour les expressions de base. Découvrez codeplex car je crois qu'ils ont un analyseur mathématique là-bas. Ou recherchez simplement BNF qui comprendra des exemples. Tout site Web présentant des concepts de compilateur inclura cela comme exemple de base.
Je sais que c'est vieux, mais je suis tombé sur cela en essayant de développer une calculatrice dans le cadre d'une plus grande application et j'ai rencontré des problèmes en utilisant la réponse acceptée. Les liens ont été immensément utiles pour comprendre et résoudre ce problème et ne doivent pas être ignorés. J'écrivais une application Android dans Java et pour chaque élément de l'expression "chaîne", j'ai en fait stocké une chaîne dans une liste de tableaux au fur et à mesure que l'utilisateur tape sur le clavier. Pour la conversion infixe en postfixe, j'ai parcouru chaque chaîne de la liste de tableaux, puis j'ai évalué la nouvelle liste de tableaux de suffixes arrangée. C'était fantastique pour un petit nombre d'opérandes/opérateurs, mais les calculs plus longs étaient systématiquement désactivés, d'autant plus que les expressions ont commencé à être évaluées en nombres non entiers. Dans le lien fourni pour conversion Infix en Postfix , il suggère de faire apparaître la pile si l'élément analysé est un opérateur et que l'élément topStack a une priorité plus élevée. I a constaté que c'était presque correct. Le fait de faire sauter l'élément topStack si sa priorité est supérieure OR ÉGAL à l'opérateur analysé a finalement rendu mes calculs corrects. J'espère que cela aidera toute personne travaillant sur ce problème, et merci à Justin Poliey (et au fas?) d'avoir fourni des liens précieux.
La question connexe analyseur d'équation (expression) avec priorité? contient également de bonnes informations sur la façon de commencer.
-Adam
Si vous avez une application "toujours active", il vous suffit de publier la chaîne mathématique sur Google et d'analyser le résultat. Un moyen simple mais je ne sais pas si c'est ce dont vous avez besoin - mais intelligent d'une certaine manière, je suppose.
ANTLR est un très bon générateur d'analyseur LL (*). Je le recommande vivement.
11 ans dans le futur à partir du moment où cette question a été posée: Si vous ne voulez pas réinventer la roue, il existe de nombreux analyseurs mathématiques exotiques.
Il y en a un que j'ai écrit il y a des années et qui prend en charge les opérations arithmétiques, la résolution d'équations, le calcul différentiel, le calcul intégral, les statistiques de base, la définition de fonction/formule, le graphique, etc.
Son appelé ParserNG et son gratuit.
L'évaluation d'une expression est aussi simple que:
MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22");
System.out.println("result: " + expr.solve());
result: 43.16981132075472
Ou en utilisant des variables et en calculant des expressions simples:
MathExpression expr = new MathExpression("r=3;P=2*pi*r;");
System.out.println("result: " + expr.getValue("P"));
Ou en utilisant des fonctions:
MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)");
System.out.println("result: " + expr.solve());
result: -10.65717648378352
Ou pour évaluer la dérivée à un point donné (Notez qu'il fait une différenciation symbolique (non numérique) dans les coulisses, donc la précision n'est pas limitée par les erreurs d'approximations numériques):
MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)");
System.out.println("result: " + expr.solve());
result: 38.66253179403897
Ce qui différencie x^3 * ln(x)
une fois à x = 3. Le nombre de fois que vous pouvez différencier est de 1 pour l'instant.
ou pour l'intégration numérique:
MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)");
System.out.println("result: " + expr.solve());
result: 7.999999999998261... approx: 8
Cet analyseur est assez rapide et possède de nombreuses autres fonctionnalités.
Le travail a été conclu sur le portage vers Swift via des liaisons à Objective C et nous l'avons utilisé pour représenter graphiquement des applications parmi d'autres cas d'utilisation itératifs.
AVERTISSEMENT: ParserNG est écrit par moi.
Les développeurs veulent toujours avoir une approche propre et essayer d'implémenter la logique d'analyse à partir de zéro, se terminant généralement par Dijkstra Shunting-Yard Algorithm . Le résultat est un code soigné, mais peut-être rempli de bogues. J'ai développé une telle API, JMEP , qui fait tout cela, mais il m'a fallu des années pour avoir un code stable.
Même avec tout ce travail, vous pouvez voir même à partir de cette page de projet que je songe sérieusement à passer à l'utilisation de JavaCC ou ANTLR, même après tout ce travail déjà fait.
En supposant que votre entrée est une expression infixe au format chaîne, vous pouvez la convertir en postfix et, en utilisant une paire de piles: une pile d'opérateurs et une pile d'opérandes, travaillez la solution à partir de là. Vous pouvez trouver des informations générales sur l'algorithme sur le lien Wikipedia.