From d24a3057ed8dc73d629a79328c0fed9ee04fa90e Mon Sep 17 00:00:00 2001 From: "bollet.c" <> Date: Tue, 20 Feb 2024 00:04:01 +0100 Subject: [PATCH] =?UTF-8?q?notation=20infixe=20qui=20fonctionne=20;=20nomb?= =?UTF-8?q?res=20n=C3=A9gatifs=20qui=20fonctionnent=20mieux=20;=20parenth?= =?UTF-8?q?=C3=A8ses=20OK=20;=20correction=20de=20bugs=20divers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expression.py | 138 ++++++++++++++++++++++++++++++++++++--------- infixe.py | 82 +++++++++++++++++++++++++++ test_interface.py | 139 +++++++++++++++++++++------------------------- 3 files changed, 257 insertions(+), 102 deletions(-) create mode 100644 infixe.py diff --git a/expression.py b/expression.py index cf715d6..5264dad 100644 --- a/expression.py +++ b/expression.py @@ -1,5 +1,6 @@ from Pile import Pile_chaine as Pile from math import * +from infixe import estUnNombre class Expression: def __init__(self, racine, gauche, droit): @@ -11,24 +12,17 @@ class Expression: def evalue(self): """renvoie la valeur de l'expression si 1 seul argument alors on prend le fils gauche""" - #fonction unaires - if self.racine == "sin": - return sin(self.gauche.evalue()) - if self.racine == "cos": - return cos(self.gauche.evalue()) - if self.racine == "tan": - return tan(self.gauche.evalue()) - #fonctions binaires - if self.racine == "+": - return self.gauche.evalue() + self.droit.evalue() - if self.racine == "-": - return self.gauche.evalue() - self.droit.evalue() - if self.racine == "*": - return self.gauche.evalue() * self.droit.evalue() - if self.racine == "/": - return self.gauche.evalue() / self.droit.evalue() - if self.racine == "^": - return self.gauche.evalue() ** self.droit.evalue() + #fonctions + if self.racine == "sin": return sin(self.gauche.evalue()) + if self.racine == "cos": return cos(self.gauche.evalue()) + if self.racine == "tan": return tan(self.gauche.evalue()) + if self.racine == "opp": return - self.gauche.evalue() + #operateurs + if self.racine == "+": return self.gauche.evalue() + self.droit.evalue() + if self.racine == "-": return self.gauche.evalue() - self.droit.evalue() + if self.racine == "*": return self.gauche.evalue() * self.droit.evalue() + if self.racine == "/": return self.gauche.evalue() / self.droit.evalue() + if self.racine == "^": return self.gauche.evalue() ** self.droit.evalue() if "." in self.racine: @@ -37,22 +31,114 @@ class Expression: def __str__(self): """affiche l'expression""" - #temporaire - if self.racine in ["+", "*", "-", "/", "^"]: - return "(" + str(self.gauche) + self.racine + str(self.droit) + ")" + priorite = {"opp":1, + "!":1, + "^":2, + "sin":3, + "cos":3, + "tan":3, + "*":4, + "/":4, + "+":5, + "-":5 + } + associativite = {"opp":"droit", + "^":"droit", + "*":"gauche", + "/":"gauche", + "+":"gauche", + "-":"gauche" + } + pas_commutatif = ["^","/","-"] + if self.racine in ["opp","!", "^", "*", "/", "+", "-"]: + gauche = str(self.gauche) + + #definitivement à améliorer + if self.droit == None: + # racine est "-" (unaire) ou "opp" + priorite_racine = priorite[self.racine] + priorite_gauche = priorite.get(self.gauche.racine, -1) + #test + print(self.gauche.racine) + #test + if self.gauche.racine[0] == "-" or priorite_racine <= priorite_gauche: + gauche = parentheses(gauche) + return "-" + gauche + + droit = str(self.droit) + + priorite_racine = priorite[self.racine] + priorite_gauche = priorite.get(self.gauche.racine, -1) + priorite_droit = priorite.get(self.droit.racine, -1) + + if self.gauche.racine == "opp" or self.gauche.racine != "-" and self.gauche.racine[0] == "-": + gauche = parentheses(gauche) + if self.droit.racine == "opp" or self.droit.racine != "-" and self.droit.racine[0] == "-": + droit = parentheses(droit) + + if priorite_racine < priorite_gauche: + gauche = parentheses(gauche) + if priorite_racine < priorite_droit: + droit = parentheses(droit) + if self.racine in pas_commutatif: + if priorite_racine == priorite_droit and associativite[self.racine] == "gauche": + droit = parentheses(droit) + if priorite_racine == priorite_gauche and associativite[self.racine] == "droit": + gauche = parentheses(gauche) + + return gauche + self.racine + droit + if self.racine in ["sin","cos","tan"]: return self.racine + "(" + str(self.gauche) + ")" return str(self.racine) - - -def npi2tree(liste_en_npi): + + +def parentheses(txt): + """ajoute des parentheses à txt""" + return "(" + txt + ")" +def npi2tree_original(liste_en_npi): """conversion d'une liste en NPI en un arbre""" pile_expr = Pile() for element in liste_en_npi: if element in ["+", "*","-","/", "^"]: - pile_expr.empiler(Expression(element, pile_expr.depiler(),pile_expr.depiler())) - elif element in ["sin","cos","tan"]: + fils_droit = pile_expr.depiler() + fils_gauche = pile_expr.depiler() + pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) + elif element in ["sin","cos","tan", "opp"]: pile_expr.empiler(Expression(element, pile_expr.depiler(),None)) else: pile_expr.empiler(Expression(element, None, None)) + return pile_expr.sommet() + +#renommer la fonction !... +def npi2tree(liste, etat_affichage): + """conversion d'une liste en NPI ou NP en un arbre""" + pile_expr = Pile() + for element in liste: + if element in ["+", "*","/", "^"]: + fils_gauche = pile_expr.depiler() + fils_droit = pile_expr.depiler() + + if etat_affichage == "post-expr" or etat_affichage == "inf": + fils_gauche, fils_droit = fils_droit, fils_gauche + + pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) + + elif element == "-": + fils_gauche = pile_expr.depiler() + if pile_expr.est_vide(): + # "-" est l'opposé + pile_expr.empiler(Expression("-", fils_gauche, None)) + else: + # "-" est la soustraction + fils_droit = pile_expr.depiler() + if etat_affichage == "post-expr" or etat_affichage == "inf": + fils_gauche, fils_droit = fils_droit, fils_gauche + + pile_expr.empiler(Expression(element, fils_gauche, fils_droit)) + + elif element in ["sin","cos","tan", "opp"]: + pile_expr.empiler(Expression(element, pile_expr.depiler(), None)) + else: + pile_expr.empiler(Expression(element, None, None)) return pile_expr.sommet() \ No newline at end of file diff --git a/infixe.py b/infixe.py new file mode 100644 index 0000000..e833ed8 --- /dev/null +++ b/infixe.py @@ -0,0 +1,82 @@ +from Pile import Pile_chaine as Pile +from math import * + +def infixe2npi(liste_en_infixe): + """conversion d'une liste en notation infixe à une liste en npi""" + pile_op = Pile() + sortie = [] + + fonctions = ["sin","cos","tan"] + operateurs = ["+", "-", "*", "/", "^"] + priorite = {"opp":1, + "^":1, + "*":2, + "/":2, + "+":3, + "-":3 + } + associativite = {"opp": "droit", + "^":"droit", + "*":"gauche", + "/":"gauche", + "+":"gauche", + "-":"gauche" + } + last_token = None + for token in liste_en_infixe: + #nombre + if estUnNombre(token): + sortie.append(token) + #fonction + elif token in fonctions: + pile_op.empiler(token) + #operateur + elif token in operateurs: + if token == "-" and not (estUnNombre(last_token) or last_token == ")"): + # "-" est l'opposé, "_" pour prefixe & postfixe + pile_op.empiler("opp") + else: + try: + while pile_op.sommet() != "(" and (priorite[pile_op.sommet()] < priorite[token] or (priorite[pile_op.sommet()] == priorite[token] and associativite[token] == "gauche")): + sortie.append(pile_op.depiler()) + except IndexError: + pass + pile_op.empiler(token) + # "," + elif token == ",": + while pile_op.sommet() != "(": + sortie.append(pile_op.depiler()) + # "(" + elif token == "(": + pile_op.empiler(token) + # ")" + elif token == ")": + try: + while pile_op.sommet() != "(": + if not pile_op.est_vide(): + sortie.append(pile_op.depiler()) + else: + raise SyntaxError + except IndexError: + raise SyntaxError + if pile_op.sommet() == "(": + pile_op.depiler() + if not pile_op.est_vide(): + if pile_op.sommet() in fonctions: + sortie.append(pile_op.depiler()) + + last_token = token + + while not pile_op.est_vide(): + if pile_op.sommet() != "(": + sortie.append(pile_op.depiler()) + else: + raise SyntaxError + + return sortie + +def estUnNombre(txt): + """indique si la chaine de caractères txt est un nombre (entier (négatif) ou flottant)""" + if txt == None : return False + if txt[0] == "-" : txt = txt.replace('-','',1) + return txt.replace('.','',1).isdigit() \ No newline at end of file diff --git a/test_interface.py b/test_interface.py index f53aeb2..c8f0b0e 100644 --- a/test_interface.py +++ b/test_interface.py @@ -3,6 +3,8 @@ from tkinter import messagebox from Pile import Pile_chaine as Pile from math import * from expression import * +from infixe import infixe2npi + class ValeurSaisie: """classe d'une valeur saise par l'utilisateur""" @@ -60,13 +62,13 @@ class Interface(tk.Frame): fonctions = tk.LabelFrame(commandes, text="fonctions", padx=5, pady=5) fonctions.pack(fill="both") - btn_sinus = tk.Button(fonctions, text='sin', command=lambda : self.evaluer("sin",1)) + btn_sinus = tk.Button(fonctions, text='sin', command=lambda : self.evaluer_stack("sin",1)) btn_sinus.pack(side="left") - btn_sinus = tk.Button(fonctions, text='cos', command=lambda : self.evaluer("cos",1)) + btn_sinus = tk.Button(fonctions, text='cos', command=lambda : self.evaluer_stack("cos",1)) btn_sinus.pack(side="left") - btn_sinus = tk.Button(fonctions, text='tan', command=lambda : self.evaluer("tan",1)) + btn_sinus = tk.Button(fonctions, text='tan', command=lambda : self.evaluer_stack("tan",1)) btn_sinus.pack(side="left") - btn_pow = tk.Button(fonctions, text='^', command=lambda : self.evaluer("^",2)) + btn_pow = tk.Button(fonctions, text='^', command=lambda : self.evaluer_stack("^",2)) btn_pow.pack(side="right") def create_menu_bar(self): @@ -109,18 +111,45 @@ class Interface(tk.Frame): self.effacer_tt() self.saisie_expression.delete(0 ,'end') - def do_something(self): - pass def aide(self): """affiche l'aide""" messagebox.showinfo("Aide", "...") def about(self): """à propos de cette application""" messagebox.showinfo("À propos", "Ceci est une calculatrice.") + def entrer(self): + """si dans mode interactif alors ajoute nv terme + si dans autre mode alors evalue l'expression""" + if self.etat_affichage == "post-int": + self.entrer_terme_dans_stack() + if len(self.saisie_expression.get()) > 0: + self.evaluer_expr() - def evaluer(self, op, nb_arg): - """affiche une erreur s'il n'y a pas assez d'arguments ; - application de l'operation aux nb_arg dernières valeurs du stack ; + def entrer_terme_dans_stack(self): + """si nombre : ajoute une nouvelle valeur dans le stack + ajoute un label avec cette valeur + si fonction : on l'évalue""" + valeur_a_ajouter = self.saisie_expression.get().replace(" ", "") + if valeur_a_ajouter != "": + if "." in valeur_a_ajouter: + valeur_a_ajouter = float(valeur_a_ajouter) + self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) + elif valeur_a_ajouter in ["sin","cos","tan","!"]: self.evaluer_stack(valeur_a_ajouter,1) + elif valeur_a_ajouter in ["+","-","/","*","^"]: self.evaluer_stack(valeur_a_ajouter,2) + elif valeur_a_ajouter in ["moy3"]: self.evaluer_stack(valeur_a_ajouter,3) + elif valeur_a_ajouter in ["somme","produit"]: self.evaluer_stack(valeur_a_ajouter,-1) + elif valeur_a_ajouter == "pi": self.stack.empiler(ValeurSaisie(pi, self.frame_stack)) + else: + try: + valeur_a_ajouter = int(valeur_a_ajouter) + self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) + except ValueError: + messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") + + self.saisie_expression.delete(0 ,'end') + + def evaluer_stack(self, op, nb_arg): + """application de l'operation aux nb_arg dernières valeurs du stack ; si nb_arg == -1 alors toutes les valeurs du stack sont utilisées""" if nb_arg == -1: if self.stack.est_vide(): @@ -149,99 +178,57 @@ class Interface(tk.Frame): if nb_arg == 1: val = self.stack.depiler().valeur - if op == "sin": - res = sin(val) - if op == "cos": - res = cos(val) - if op == "tan": - res = tan(val) - if op == "!": - res = factorial(val) + if op == "sin": res = sin(val) + if op == "cos": res = cos(val) + if op == "tan": res = tan(val) + if op == "sqrt": res = sqrt(val) + if op == "!": res = factorial(val) elif nb_arg == 2: droite = self.stack.depiler().valeur gauche = self.stack.depiler().valeur - if op == "+": - res = gauche + droite - if op == "-": - res = gauche - droite - if op == "*": - res = gauche * droite - if op == "/": - res = gauche / droite - if op == "^": - res = gauche ** droite + if op == "+": res = gauche + droite + if op == "-": res = gauche - droite + if op == "*": res = gauche * droite + if op == "/": res = gauche / droite + if op == "^": res = gauche ** droite elif nb_arg == 3: terme3 = self.stack.depiler().valeur terme2 = self.stack.depiler().valeur terme1 = self.stack.depiler().valeur - if op == "moy3": - res = (terme1 + terme2 + terme3)/3 + if op == "moy3": res = (terme1 + terme2 + terme3)/3 self.stack.empiler(ValeurSaisie(res, self.frame_stack)) - def entrer(self): - """si dans mode npi: si dans interactif alors ajoute nv terme - si dans expression alors evalue l'expression""" - #completer les différents cas pour les autres notations - if self.etat_affichage == "post-int": - self.entrer_terme_dans_stack() - if self.etat_affichage == "post-expr": - self.evaluer_prepostfixe() - if self.etat_affichage == "pre": - self.evaluer_prepostfixe() - def evaluer_prepostfixe(self): + def evaluer_expr(self): """evalue la chaine de caractère dans le champ de saisie - comme une expression en notation polonaise ou npi selon l'affichage""" - #simplifier les conditions (plus tard) - ça m'a l'air pas mal + selon l'affichage / la notation choisie (prefixe/infixe/postfixe)""" lst_expr = self.saisie_expression.get().split() if self.etat_affichage == "pre": - lst_expr = lst_expr.reverse() - + lst_expr.reverse() + if self.etat_affichage == "inf": + try: + lst_expr = infixe2npi(lst_expr) + except SyntaxError: + messagebox.showerror("Erreur de syntaxe", "L'expression est mal parenthésée, ou vous avez oublié une espace") + return try: - arbre_expr = npi2tree(lst_expr) + arbre_expr = npi2tree(lst_expr, self.etat_affichage) self.affichage_expression.config(text = str(arbre_expr) + "=" + str(arbre_expr.evalue())) except IndexError: - messagebox.showerror("Erreur","Erreur") - - def entrer_terme_dans_stack(self): - """si nombre : ajoute une nouvelle valeur dans le stack - ajoute un label avec cette valeur - si fonction : appelle la fonction évaluer""" - valeur_a_ajouter = self.saisie_expression.get().replace(" ", "") - if valeur_a_ajouter != "": - if "." in valeur_a_ajouter: - valeur_a_ajouter = float(valeur_a_ajouter) - self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) - elif valeur_a_ajouter in ["sin","cos","tan","!"]: - self.evaluer(valeur_a_ajouter,1) - elif valeur_a_ajouter in ["+","-","/","*","^"]: - self.evaluer(valeur_a_ajouter,2) - elif valeur_a_ajouter in ["moy3"]: - self.evaluer(valeur_a_ajouter,3) - elif valeur_a_ajouter in ["somme","produit"]: - self.evaluer(valeur_a_ajouter,-1) - elif valeur_a_ajouter == "pi": - self.stack.empiler(ValeurSaisie(pi, self.frame_stack)) - else: - try: - valeur_a_ajouter = int(valeur_a_ajouter) - self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) - except ValueError: - messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") - - self.saisie_expression.delete(0 ,'end') + messagebox.showerror("Erreur","Erreur de syntaxe") + except ValueError: + messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") def effacer_tt(self): """supprime tout le contenu de la pile (et les labels associés)""" while not self.stack.est_vide(): self.stack.depiler() self.frame_stack.config(height = 1) - - + def effacer_dernier(self): """supprime la dernière valeur entrée (et le label associés)""" if not self.stack.est_vide():