8
votes

Python - Évaluez l'expression mathématique dans la chaîne

J'ai une question concernant l'évaluation de l'expression mathématique dans une chaîne. Par exemple, ma chaîne suit:

'I have 30 apples'


4 commentaires

Stackoverflow.com/Questtions/2371436 / ...


@Jeffery_the_wind Pas vraiment sur le point, comme cela (contrairement à cela) nécessite de jeter des parties non mathématiques de la chaîne.


Pourriez-vous utiliser 'j'ai {{6 * (2 + 3)}} pommes' comme format d'entrée?


Cela pourrait bien fonctionner avec un bon remplacement de la fonction eval () : CODEPAD.ORG/QOHSPWZH < / a>


8 Réponses :


1
votes

C'est un problème très délicat qui est probablement presque impossible à résoudre en général. Cependant, voici un moyen simple d'attaquer le problème qui fonctionne avec l'exemple d'entrée.

* Étape 1 - assainir l'entrée. C'est la partie la plus difficile à faire en général. Fondamentalement, vous avez besoin d'un moyen de tirer une expression mathématique unique de la chaîne sans le gérer. Ici, une simple regex fonctionnera: p> xxx pré>

* étape 2 - Évaluez à l'aide de EVAL CODE>: P>

new_string = my_str.replace(sanitized, str(value))


8 commentaires

Eh bien, ce n'est pas que cela ne puisse pas être résolu en général. C'est un problème mal défini tel que (ce qui constitue une expression d'être évalué et ce qui ne va pas?) Mais une fois que nous avons cloué cela, il est facilement résolu, si vous vous souciez de paquerer des objets au lieu d'abuser eval < / code>.


@delnan - Si ceci est eval abus, alors quels eval abus (à quel point ne devrait-il pas être retiré de la langue complètement?). Je pense que ceci est un endroit complètement fin à utiliser eval étant donné que le problème est suffisamment contraint pour analyser l'expression à évaluer hors de la chaîne d'entrée.


Je pense que eval ne doit pas être placé aussi bien en évidence (c'est-à-dire dans l'espace de noms global). Je connais des cas d'utilisation pour compile et exécutant (et ils sont significativement différents de ceci, en ce sens qu'ils contrôlent la chaîne d'entrée de 100% et on sait ce qu'il va faire). Je n'ai pas encore rencontré un cas d'utilisation de bon utilisation pour eval - lorsque vous souhaitez évaluer une expression mathématique, écrivez un évaluateur de coureurs de courage ou quelque chose. Si vous souhaitez exécuter un code Python, utilisez exécuté car il est moins restreint. Je n'évite pas eval pour l'exactitude, mais pour ne pas mélanger le code avec données. (Vissez les gars LISP qui disent que le code est des données.)


@delnan - Écrivez votre propre algorithme de cour de courage? Vraiment? Pourquoi traverser tout ce travail pour créer et déboguer quelque chose qui est déjà (mis en évidence) dans la bibliothèque standard? Je comprends l'argument selon lequel vous ne savez pas ce que vous sortirez, mais c'est vrai dans une certaine mesure avec n'importe quelle fonction en Python en raison de la typographie de canard et de la surcharge de l'opérateur. La morale est de mettre de bonnes intrants dans vos fonctions et vous obtiendrez de bonnes choses. Notez également qu'ici '6 * (2 + 3)' n'est pas un code. C'est une corde. Nous n'essayons pas de modifier le flux de programme avec cela ou quoi que ce soit ...


Pour revenir, mon point principal était que de nombreuses définitions utiles de ce qui devrait être évaluée nécessite une autre analyse supplémentaire (cf. Les règles relatives au moment où un * dans restructurétext ne constitue qu'un caractère et non une construction de balisage). Et oui, 6 * (2 + 3) est une chaîne - pas une expression python. C'est exactement pourquoi je m'oppose à la transmettre à eval . En outre, SY n'est pas terriblement difficile à mettre en œuvre compte tenu des descriptions de détails de celui-ci (je l'ai fait moi-même) et facilement testé. Il y a une limite raisonnable sur ce qui peut se produire avec une fonction comme celle-là, ou la plupart des autres fonctions. Avec eval etc., la seule limite est python.


Fais attention. Nous avons tous bu la "eval est mauvais" Koolaid si longtemps, il est parfois difficile de reculer et de réaliser que les Devs Python le laissent là-bas pour une raison (principalement qu'elle est utile). Comme tous les outils puissants (par exemple, dynamite, scies à table ...) Vous devez manipuler avec précaution, mais "avec eval la seule limite est Python" n'est vrai que pour un Bare eval . Si vous passez un dictionnaire des globaux, vous limitez l'espace de noms que eval a accès à l'utilisation et (AFAIK), vous pouvez même l'utiliser en toute sécurité (comme je l'ai démontré dans ma réponse). Mais peu importe ...


@Delnan - Vous avez raison que la partie la plus difficile de ce problème est en train de déterminer quelles chaînes devraient être traitées comme des expressions et qui ne sont que des cordes. Après avoir eu l'expression comme une chaîne seule, elle devient beaucoup plus facile (en utilisant eval, pyparsing ou autre chose ... - le problème est alors bien défini au moins.)


Laissez-nous Continuer cette discussion en chat



2
votes

Voici ma tentative: xxx

notes:

C'est très simple, il ne traitera pas d'équations complexes - comme celles avec racine carrée, pi, etc. . Mais je crois que c'est dans l'esprit de la question suivante. Pour un vraiment réponse robuste, voir le question postée par Jeffery_the_wind ; Mais je crois que cela peut être surchargé pour ce cas simpliste.


0 commentaires

0
votes

Pour une solution sans utiliser Eval, voici ce que je ferais. Commencez par rechercher toutes les expressions mathématiques de la chaîne, que je définirai comme une chaîne contenant des espaces, des parenthèses, des chiffres et des opérations, puis éteignez les matchs qui sont tous blouses:

>>> new_str = my_str
>>> for expr, res in sorted(zip(exprs, results), key=lambda t: t[0].start(), reverse=True):
...     new_str = new_str[:expr.start()] + (" %d " % res) + new_str[expr.end():]
... 
>>> new_str
'I have 30 apples'


0 commentaires

2
votes

Parfois, il vaut mieux simplifier la question plutôt que de trouver des solutions complexes. Vous voudrez peut-être simplifier le problème en faisant preuve de votre code comme celui-ci xxx

de cette façon, vous pouvez l'analyser à l'aide d'une simple regex et d'eval, ce qui est à l'intérieur. Sinon, vous avez beaucoup de complexité.


0 commentaires

0
votes

Mon option:

>>> import re
>>> def calc(s):
...     val = s.group()
...     if not val.strip(): return val
...     return " %s " % eval(val.strip(), {'__builtins__': None})
>>> re.sub(r"([0-9\ \.\+\*\-\/(\)]+)", calc, "I have 6 * (2 + 3 ) apples")
'I have 30 apples'


0 commentaires

1
votes

Merci à tous pour votre aide. En fait, mon exemple fourni est très simple comparé ce que j'ai dans une tâche réelle. J'ai lu ces chaîne à partir de fichier et parfois peut avoir une vue comme ceci:

def eval_math_expressions(filelist):
        for line in filelist:
              if re.match('.*[\-|\+|\*|\/].*',line):
                        lindex=int(filelist.index(line))
                        line_list=line.split()
                        exp=[]
                        for word in line_list:
                                if re.match('^\(+\d+',word) or re.match('^[\)+|\d+|\-|\+|\*|\/]',word):
                                        exp.append(word)
                                else:
                                        ready=' '.join(exp)
                                        if ready:
                                                eval_ready=str(eval(ready))
                                                line_new=line.replace(ready,eval_ready)
                                                line=line_new
                                                filelist[lindex]=line
                                        exp=[]
        return filelist


0 commentaires

-1
votes

[Je sais que c'est une question ancienne, mais il convient de souligner de nouvelles solutions utiles telles qu'elles apparaissent]

Depuis Python3.6, cette capacité est maintenant intégrée à la langue , inventée "f-strings" .

Voir: PEP 498 - Interpolation de chaîne littérale

Par exemple (notez le F préfixe): xxx


0 commentaires

1
votes

Utilisez F-Strings ou Tranchez.

F-String: f'I a {str (6 * (2 + 3))} pommes '


0 commentaires