J'avais besoin d'aide pour créer des arbres personnalisés étant donné une expression arithmétique. Supposons, par exemple, que vous saisissez cette expression arithmétique:
(5+2)*7
L'arbre des résultats devrait ressembler à:
*
/ \
+ 7
/ \
5 2
J'ai des classes personnalisées pour représenter les différents types de nœuds, c'est-à-dire PlusOp, LeafInt, etc. Je n'ai pas besoin d'évaluer l'expression, il suffit de créer l'arborescence, afin que je puisse y exécuter d'autres fonctions plus tard. De plus, l'opérateur négatif '-' ne peut avoir qu'un seul enfant, et pour représenter '5-2', vous devez le saisir comme 5 + (-2).
Une certaine validation de l'expression serait nécessaire pour garantir que chaque type d'opérateur a le bon non. d'arguments/enfants, chaque parenthèse ouvrante est accompagnée d'une parenthèse fermante.
De plus, je devrais probablement mentionner que mon ami a déjà écrit du code qui convertit la chaîne d'entrée en une pile de jetons, si cela peut être utile pour cela.
J'apprécierais toute aide. Merci :)
(J'ai lu que vous pouvez écrire une grammaire et utiliser antlr/JavaCC, etc. pour créer l'arborescence d'analyse, mais je ne suis pas familier avec ces outils ou avec l'écriture de grammaires, donc si c'est votre solution, je vous serais reconnaissant si vous pourrait leur fournir des tutoriels/liens utiles.)
"Introduction de cinq minutes à ANTLR" inclut un exemple de grammaire arithmétique. Cela vaut la peine de le vérifier, d'autant plus que antlr est open source (licence BSD).
En supposant que ce soit une sorte de devoir et que vous voulez le faire vous-même ..
J'ai fait ça une fois, tu as besoin d'une pile
Donc, ce que vous faites pour l'exemple est:
analyser quoi faire? La pile ressemble à (Poussez-la sur la pile ( 5 Poussez 5 (, 5 + Poussez + (, 5, + 2 Poussez 2 (, 5 , +, 2 ) Évaluer jusqu'à (7 * Appuyer sur * 7, * 7 Appuyer sur 7 +7, *, 7 Eof évaluer jusqu'au sommet 49
Les symboles tels que "5" ou "+" peuvent simplement être stockés sous forme de chaînes ou d'objets simples, ou vous pouvez stocker le + en tant qu'objet + () sans définir les valeurs et les définir lors de l'évaluation.
Je suppose que cela nécessite également un ordre de priorité, donc je vais décrire comment cela fonctionne.
dans le cas de: 5 + 2 * 7
vous devez appuyer sur Push 5 Push + Push 2. L'opération suivante a une priorité plus élevée, vous devez donc le pousser également, puis pousser les trois. Lorsque vous rencontrez a) ou la fin du fichier ou un opérateur de priorité inférieure ou égale, vous commencez à calculer la pile à la précédente (ou au début du fichier).
Parce que votre pile contient maintenant 5 + 2 * 7, lorsque vous l'évaluez, vous éclatez d'abord le 2 * 7 et poussez le nœud * (2,7) résultant sur la pile, puis une fois de plus, vous évaluez les trois premiers éléments de la pile ( 5 + * nœud) pour que l'arbre soit correct.
S'il était ordonné dans l'autre sens: 5 * 2 + 7, vous pousseriez jusqu'à ce que vous arriviez à une pile avec "5 * 2", puis vous atteindriez la priorité inférieure + ce qui signifie évaluer ce que vous avez maintenant. Vous évalueriez le 5 * 2 en un noeud * et le pousseriez, puis vous continueriez en appuyant sur le + et le 3 pour avoir * le noeud + 7, à quel point vous évalueriez cela.
Cela signifie que vous avez une "priorité actuelle la plus élevée" qui stocke un 1 lorsque vous appuyez sur +/-, un 2 lorsque vous appuyez sur * ou/et un 3 pour "^". De cette façon, vous pouvez simplement tester la variable pour voir si la priorité de votre prochain opérateur est <= votre priorité actuelle.
si ")" est considéré comme la priorité 4, vous pouvez le traiter comme d'autres opérateurs, sauf qu'il supprime la correspondance "(", une priorité inférieure ne le ferait pas.
Je voulais répondre à la réponse de Bill K., mais je n'ai pas la réputation d'y ajouter un commentaire (c'est vraiment là que cette réponse appartient). Vous pouvez considérer cela comme un addendum à la réponse de Bill K., car la sienne était un peu incomplète. La considération manquante est associativité des opérateurs ; à savoir, comment analyser des expressions comme:
49 / 7 / 7
Selon que la division est associative gauche ou droite, la réponse est:
49 / (7 / 7) => 49 / 1 => 49
ou
(49 / 7) / 7 => 7 / 7 => 1
En règle générale, la division et la soustraction sont considérées comme associatives à gauche (c'est-à-dire le cas deux, ci-dessus), tandis que l'exponentiation est associative à droite. Ainsi, lorsque vous rencontrez une série d'opérateurs avec une priorité égale, vous souhaitez les analyser dans l'ordre s'ils sont laissés associatifs ou dans l'ordre inverse s'ils sont associatifs droits. Cela détermine simplement si vous poussez ou sautez dans la pile, de sorte qu'il ne complique pas trop l'algorithme donné, il ajoute simplement des cas où les opérateurs successifs ont une priorité égale (c.-à-d. Évaluer la pile si associative gauche, pousser sur la pile si associative droite) .
Plusieurs options pour vous:
Réutilisez un analyseur d'expression existant. Cela fonctionnerait si vous êtes flexible sur la syntaxe et la sémantique. Un bon que je recommande est le langage d'expression unifié intégré à Java (initialement pour une utilisation dans les fichiers JSP et JSF).
Écrivez votre propre analyseur à partir de zéro. Il existe un moyen bien défini d'écrire un analyseur qui prend en compte la priorité des opérateurs, etc. Décrire exactement comment cela se fait sort du cadre de cette réponse. Si vous suivez cette voie, trouvez-vous un bon livre sur la conception du compilateur. La théorie de l'analyse syntaxique du langage sera abordée dans les premiers chapitres. En règle générale, l'analyse syntaxique des expressions est l'un des exemples.
Utilisez JavaCC ou ANTLR pour générer un lexer et un analyseur. Je préfère JavaCC, mais à chacun le leur. Il suffit de google "javacc samples" ou "antlr samples". Vous en trouverez plein.
Entre 2 et 3, je recommande fortement 3 même si vous devez apprendre de nouvelles technologies. Il y a une raison pour laquelle des générateurs d'analyseurs ont été créés.
Notez également que la création d'un analyseur capable de gérer des entrées mal formées (et pas seulement d'échouer avec une exception d'analyse) est beaucoup plus compliquée que l'écriture d'un analyseur qui n'accepte que des entrées valides. Vous devez essentiellement écrire une grammaire qui énonce les diverses erreurs de syntaxe courantes.
Mise à jour: Voici un exemple d'un analyseur de langage d'expression que j'ai écrit en utilisant JavaCC. La syntaxe est vaguement basée sur le langage d'expression unifié. Cela devrait vous donner une assez bonne idée de ce que vous affrontez.
l'expression donnée (5 + 2) * 7 nous pouvons prendre comme infixe
Infix : (5+2)*7
Prefix : *+527
à partir de ce qui précède, nous connaissons la pré-commande et l'inaversion de l'ordre des arbres ... et nous pouvons facilement construire un arbre à partir de cela. Merci,