Évaluation des expressions mathématiques dans Python
-
15-11-2019 - |
Question
Je veux tokeniser une expression mathématique donnée dans un arbre d'analyse comme ceci:
((3 + 4 - 1) * 5 + 6 * -7) / 2
'/'
/ \
+ 2
/ \
* *
/ \ / \
- 5 6 -7
/ \
+ 1
/ \
3 4
Y a-t-il une pure façon de faire de Python de le faire? Comme passer comme une chaîne à Python, puis récupérer comme un arbre comme mentionné ci-dessus.
Merci.
La solution
Oui, le Python ast
Le module fournit des installations pour ce faire. Vous devrez rechercher l'interface exacte de votre version de Python, puisque la ast
Le module semble changer régulièrement.
En particulier, le ast.parse()
La méthode sera utile pour votre application:
>>> import ast
>>> ast.parse("(1+2)*3", "", "eval")
<_ast.Expression object at 0x88950>
>>> ast.dump(_)
'Expression(body=BinOp(left=BinOp(left=Num(n=1), op=Add(), right=Num(n=2)), op=Mult(), right=Num(n=3)))'
Autres conseils
Plusieurs cadres d'analyseurs existent pour Python; Certains communs sont PLI et pypars. Ned Batchelder a un Liste assez complète.
Il existe de nombreux bons algorithmes établis pour analyser les expressions mathématiques comme celle-ci. Un particulièrement bon est Dijkstra algorithme de charité, qui peut être utilisé pour produire un tel arbre. Je ne connais pas une implémentation particulière dans Python, mais l'algorithme n'est pas particulièrement complexe et il ne devrait pas prendre trop de temps pour en faire un.
Soit dit en passant, le terme le plus précis pour l'arbre que vous construisez est un analyse d'analyse ou Syntaxe abstrait.
Vous pouvez le faire avec le module Python AST.
https://docs.python.org/3.6/library/ast.html
La théorie est notre opération mathématique que nous voulons évaluer, nous utilisons l'isinstance afin de connaître le type qu'il est, si c'est un nombre, si c'est un opérateur binaire (+, *, ..). Vous pouvez lire à https://greentreesnakes.readthedocs.io/en/latest/tofrom.html , comment fonctionne l'AST
Et afin de faire fonctionner la méthode que nous devrions utiliser: évaluer (ast.parse (theoperation, mode = 'eval'). Corps)
def evaluate(theoperation):
if (isinstance(theoperation, ast.Num)):
return theoperation.n
if (isinstance(theoperation, ast.BinOp)):
leftope= evaluate(theoperation.left)
rightope=evaluate(theoperation.right)
if (isinstance(theoperation.op, ast.Add)):
return left+right
elif (isinstance(theoperation.op, ast.Sub)):
return left-right
elif (isinstance(theoperation.op, ast.Mult)):
return left*right
elif (isinstance(theoperation.op, ast.Div)):
return left/right
elif (isinstance(theoperation.op, ast.Pow)):
return left**right
Je ne connais pas une façon "pure python" de le faire, qui est déjà implémentée pour vous. Cependant, vous devriez consulter antlr (http://www.antlr.org/), c'est un analyseur open source un lexer et il a une API pour un certain nombre de langues, y compris Python. De plus, ce site Web propose d'excellents didacticiels vidéo qui vous montreront comment faire exactement ce que vous demandez. C'est un outil très utile pour savoir comment utiliser en général.