Objectifs du projet
L'ambtiion est de développer un analyseur syntaxique capable d'évaluer des expressions mathématiques complexes incluant les opérations fondamentales (addition, soustraction, multiplication, division) tout en respectant la priorité des parenthèses. L'implémentation doit traiter des formules telles que :
1 - 2 * ( (6-3 +(-5/5)*(9-2*3/3 + 7/3*7/4*12 +10 * 5/5 )) - (-4*3)/ (12-3*2) )
Le système doit produire un résultat identique à celui d'une calculatrice scientifique standard.
Approche algorithmique
La stratégie retenue consiste à traiter récursivement les groupes de parenthèses en commençant par les plus imbriqués. Pour chaque groupe identifié, on procède à une évaluation en deux phases : résolution des opérations de multiplication et division, puis traitement des additions et soustractions.
Implémentation
Le code suivant propose une solution encapsulée dans une classe dédiée, offrant une meilleure modularité.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
class AnalyseurCalcul:
"""Classe principale pour l'évaluation d'expressions arithmétiques."""
def __init__(self, expression):
"""Initialisation avec nettoyage des espaces."""
self.expr = expression.replace(' ', '')
def _resoudre_multiplication_division(self, terme):
"""Traite les opérations * et / au sein d'un terme."""
elements = re.split('([*/])', terme)
if not elements or len(elements) == 0:
return 0.0
resultat = float(elements[0])
for i in range(1, len(elements), 2):
operateur = elements[i]
nombre = float(elements[i+1])
if operateur == '*':
resultat *= nombre
else:
resultat /= nombre
return resultat
def _resoudre_addition_soustraction(self, sous_expr):
"""Gère les opérations + et - avec gestion des signes initiaux."""
# Gestion du signe préfixe
signe_initial = '+'
if sous_expr.startswith(('+', '-')):
signe_initial = sous_expr[0]
sous_expr = sous_expr[1:]
# Décomposition en opérandes et opérateurs
parties = re.split('([+-])', sous_expr)
if signe_initial == '-':
parties[0] = '-' + parties[0]
# Filtrage des éléments vides
parties = [p for p in parties if p]
# Calcul itératif
total = float(parties[0])
for i in range(1, len(parties), 2):
op = parties[i]
val = float(parties[i+1])
total += val if op == '+' else -val
return total
def _evaluer_groupe(self, groupe):
"""Évalue le contenu d'un groupe de parenthèses."""
contenu = groupe[1:-1] # Suppression des parenthèses
# Si présence de multiplication/division
if '*' in contenu or '/' in contenu:
# Décomposition en termes séparés par + ou -
termes = re.split('([+-])', contenu)
resultats_termes = []
i = 0
while i < len(termes):
terme = termes[i]
if terme in '+-':
resultats_termes.append(terme)
i += 1
continue
# Résolution des opérations de priorité
if '*' in terme or '/' in terme:
val = self._resoudre_multiplication_division(terme)
resultats_termes.append(str(val))
else:
resultats_termes.append(terme)
i += 1
# Évaluation finale des additions/soustractions
expression_simplifiee = ''.join(resultats_termes)
return self._resoudre_addition_soustraction(expression_simplifiee)
else:
# Uniquement additions/soustractions
return self._resoudre_addition_soustraction(contenu)
def calculer(self):
"""Boucle principale d'évaluation."""
iteration = 0
max_iterations = 20
while '(' in self.expr and iteration < max_iterations:
# Localisation du groupe de parenthèses le plus profond
match = re.search(r'\([^()]+\)', self.expr)
if not match:
break
groupe = match.group()
resultat = self._evaluer_groupe(groupe)
# Substitution dans l'expression originale
self.expr = self.expr.replace(groupe, str(resultat), 1)
# Normalisation des signes
self.expr = self.expr.replace('+-', '-').replace('--', '+')
iteration += 1
print(f"Étape {iteration}: {self.expr}")
# Traitement final sans parenthèses
if '*' in self.expr or '/' in self.expr:
termes = re.split('([+-])', self.expr)
resultats = []
i = 0
while i < len(termes):
element = termes[i]
if element in '+-':
resultats.append(element)
i += 1
continue
if '*' in element or '/' in element:
val = self._resoudre_multiplication_division(element)
resultats.append(str(val))
else:
resultats.append(element)
i += 1
self.expr = ''.join(resultats)
# Évaluation terminale
return self._resoudre_addition_soustraction(self.expr)
# Programme principal
if __name__ == '__main__':
formule_complexe = '1 - 2 * ( (6-3 +(-5/5)*(9-2*3/3 + 7/3*7/4*12 +10 * 5/5 )) - (-4*3)/ (12-3*2) )'
analyseur = AnalyseurCalcul(formule_complexe)
resultat_final = analyseur.calculer()
print(f"\nRésultat final: {resultat_final}")
Trace d'exécution
L'évlauation progressive produit les transformations suivantes :
Étape 1: 1-2*((6-3-1.0*(9-2*3/3+7/3*7/4*12+10*1.0))-(-4*3)/(12-3*2))
Étape 2: 1-2*((6-3-1.0*(9-2.0+49.00000000000001+10.0))-(-4*3)/(12-3*2))
Étape 3: 1-2*((6-3-1.0*66.0)-(-4*3)/(12-3*2))
Étape 4: 1-2*((6-3-66.0)-(-4*3)/(12-3*2))
Étape 5: 1-2*(-63.0-(-4*3)/(12-3*2))
Étape 6: 1-2*(-63.0-(-12.0)/(12-3*2))
Étape 7: 1-2*(-63.0+12.0/(12-3*2))
Étape 8: 1-2*(-63.0+12.0/(12-6.0))
Étape 9: 1-2*(-63.0+12.0/6.0)
Étape 10: 1-2*(-63.0+2.0)
Étape 11: 1-2*-61.0
Étape 12: 1+122.0
Résultat final: 123.0