8
votes

Analyser une équation avec des fonctions personnalisées en python

J'ai une chaîne qui est une équation mathématique, mais avec des fonctions personnalisées. J'ai besoin de trouver toutes ces fonctions et de les remplacer avec du code.

Par exemple, j'ai une chaîne: xxx

Je veux le code qui remplacera (dire) < Code> F2 (x, y) avec x + y ^ 2 et f1 (x, y) avec sin (x + y) < /Code >.

Il serait idéal si des fonctions imbriquées étaient prises en charge, comme dans l'exemple. Cependant, il serait toujours utile que la nidification n'était pas prise en charge.

Si que je comprends à partir de sujets similaires, vous pouvez être effectué à l'aide d'un module compilateur tel que compiler.parse (EQ) . Comment puis que je puisse travailler avec AST objet créé par compiler.parse (EQ) pour reconstruire mon chaîne en arrière, remplacer toutes les fonctions trouvées?

J'ai besoin de remplacer la substitution et ensuite la chaîne être utilisé dans un autre programme. L'évaluation n'est pas nécessaire.


19 commentaires

Afin de reconstruire votre chaîne, vous devez utiliser Traversal LVR sur l'AST donné.


Pourquoi ne pas recueillir la chaîne?


@Maher c'est exactement la question. Comment puis-je effectuer Traversal LVR. J'ai besoin d'un exemple.


@Styvane Si je vais simplifier la corde, je pense que je serai incapable de travailler avec des fonctions imbriquées?


Hors de curiosité: essayez-vous de le faire pour une sorte d'algorithme évolutif?


@Alex Connaissez-vous chaque séquence de texte possible dans votre chaîne?


@ inspectorg4dget n ° N ° voulue simplement faire une automatisation pour la génération des mesures.


Êtes-vous ouvert à la représentation de vos fonctions dans un autre format que comme chaîne?


@Styvane Je connais toutes les fonctions possibles et toutes les opérations mathématiques mathématiques (+ - * /), mais elles peuvent venir dans n'importe quelle séquence et les équations peuvent être utilisées comme arguments de fonctions. De cause maintenant j'ai la mise en œuvre de la fonction singulaire. C'est simple. Je suppose que je trouverai la voie à traiter avec plusieurs fonctions sans fonctions imbriquées. Mais j'aimerais trouver une approche universelle qui fonctionnera sans limitations.


@ inspectorg4dget je pense que non. Les humains doivent être capables d'écrire des équations :) Je suppose qu'ils ne voudront pas créer quelque chose qui n'est pas une chaîne :)


N'avez pas le temps d'écrire la solution en ce moment, mais jetez un coup d'œil à la fonction parse ICI . Il y a quelque chose là-bas que vous pouvez étendre à votre but


Je suppose que les formules ne sont pas très grandes (~~ 1 ligne)? Pourquoi ne pouvez-vous pas faire cela de manière triviale avec une substitution de Regexp (peut-être nidifier)? Si vous ne pouvez pas, je peux vous montrer une réponse non python qui peut faire des remplaçants arbitraires, y compris les remplaçants «imbriqués», tous basés sur des asts.


Que pense votre programme externe des ^ et ** opérateurs?


@Irabaxter Il est difficile de traiter des fonctions à l'intérieur des fonctions et de plusieurs niveaux de parenthèses avec REGEXP. La programmation n'est également pas mon travail principal et j'ai besoin d'une solution relativement simple (pas de consommation de temps).


@ERIC ^ est la fonction xor ** est un exposant


Vous décidez-vous si le résultat a des parenthèses redondantes? C'est-à-dire que si vous donnez la fonction a + b * x , étiez-vous si vous avez récupéré (A + (b * x)) (indépendant de la substitution de la fonction)? Ou en d'autres termes, l'expression convertie est-elle destinée à un traitement ultérieur ou à l'affichage des êtres humains?


Et une autre question: vos fonctions ont-elles des variables gratuites?


@ici l'expression convertie destinée à un traitement ultérieur, mais également affichée aux êtres humains. Mais les parenthèses redondantes peuvent être ok.


@ici ce que tu veux dire par des variables libres?


6 Réponses :


1
votes

Quel est votre objectif à long terme? Est-ce d'évaluer la fonction ou d'effectuer simplement une substitution? Dans le premier cas, vous pouvez simplement essayer ceci (note que F1 et F2 pourrait également être défini de manière dynamique): xxx

si tu veux Pour remplacer les fonctions et récupérer la version modifiée, vous devrez effectivement recourir à une sorte d'analyseur ast. Soyez prudent avec l'utilisation de eval , car cela ouvre un trou de sécurité pour le code d'entrée d'utilisateur malveillant.


1 commentaires

J'ai besoin que de remplacer la substitution. L'évaluation sera effectuée avec un autre programme (simulateur)




3
votes

Connaissez-vous les variables à l'avance?

Je recommande d'utiliser Sydy! P>

Prenons par exemple les éléments suivants: P>

import sympy

a,b,x,y = sympy.symbols('a b x y')
f1 = sympy.Function('f1')
f2 = sympy.Function('f2')

readString = "a+b+f1(f2(x,y),x)"

z = eval(readString)


1 commentaires

Généralement intéressant. Mais dans mon cas, je ne sais pas les variables à l'avance.



8
votes

Voici un exemple de travail minimal ( +, -, *, /, ** code> des opérations binaires et uniques et des appels de fonction mises en œuvre). La priorité des opérations est définie avec des parenthèses.

Un peu plus que la fonctionnalité de l'exemple donné est effectuée: p> xxx pré>

USAGE: P>

((-(a+b)+(((sin((+x+y))+(z**2))*4)/365.12))-h)


2 commentaires

Semble fonctionner bien. Actuellement, le seul problème mineur est des parenthèses redondantes, mais ça va toujours bien. Merci.


Pour supprimer les parenthèses redondantes, vous devez tenir compte de la priorité de l'opérateur. Je préférais ne pas faire. L'expression est corenconnée même si cela pourrait être simplifié



1
votes

(en utilisant symboly comme Adrianx suggéré, avec un certain code supplémentaire.)

Le code ci-dessous convertit une chaîne donnée en une nouvelle chaîne après avoir combiné les fonctions données. C'est hâtif et mal documenté, mais ça marche .


avertissement!

contient EXEC eval , le code malveillant pourrait probablement avoir une Effectué, si l'entrée est fournie par des utilisateurs externes.


update:

  • réécrit le code entier. Travaille à Python 2.7.
  • Les arguments de la fonction peuvent être séparés par une virgule ou une espace blanche ou les deux.
  • Tous les exemples en question et les commentaires fonctionnent.
    xxx

9 commentaires

J'ai essayé initial_str = 'A + myF1 (myF2 (a, b), y)' function_names = {'myf1 (x, y)': 'croix (x, y)', 'myf2 (a, b) ':' valeur (A, b) '} mais a eu une erreur


@ALEX Le problème est que valeur et cross n'existe pas dans la sympty. Je l'ai mis en mesure de reconnaître toutes les fonctions de sympty ( SIN, COT, SQRT , etc.). Peut-être ont-ils un nom différent. Je suppose que cross est "Cross Product", correct? Quelle est la valeur cependant?


@Alex Il peut probablement être corrigé si je fais une dicte avec des noms personnalisés tels que Cross correspondant au nom équivalent dans Sydy. Mon mauvais pour les pauvres docstrings que j'ai utilisés. At-il fonctionné pour d'autres noms de fonction?


Comme je l'ai écrit, une fois les résultats de traduction seront utilisés dans un autre programme, le programme est un simulateur analogique. Donc, la fonction croisée est la fonction de croisement de deux signaux analogiques et la fonction de valeur donne une valeur Y du signal donné à X. Ce dont j'ai besoin est une traduction générale de tout nom de fonction arbitraire sur tout autre nom de fonction arbitraire. Des noms de toutes les fonctions connues à l'avance


Presque. Mais la réponse acceptée ajoute beaucoup de parenthèses redondantes et une nouvelle fonction nécessite une virgule comme séparateur entre les arguments lorsque j'ai vraiment besoin d'espace comme séparateur.


@Alex Bien que vous ayez déjà accepté une réponse différente de celle de la mienne, et rien pour moi de gagner, je vais essayer de vous aider. Cependant, essayez d'être plus précis. J'ai vraiment besoin d'espace comme séparateur - c'est une nouvelle exigence que vous n'avez pas mentionnée. Donne un exemple.


Merci pour vos efforts. Je pense que ce sera utile non seulement pour moi, mais aussi pour d'autres personnes ayant le même problème. Je vois déjà quatre ajoutés cette question aux favoris. Je pense que je ne m'avais pas bien expliqué sur l'espace comme séparateur. Le problème est que je voudrais généralement remplacer la fonction par fonction comme je voudrais remplacer C (x, y) par croix (x y a b c). Donc, séparateur de virgule, bon pour la fonction source, mais dans la fonction de destination, j'ai besoin d'espace comme séparateur.


Car il est possible de remplacer toutes les virgules par des espaces après la substitution. Mais ce sera bien si je serai capable de spécifier la fonction de destination avec des espaces à l'origine. Pour une question acceptée, j'ai besoin d'écrire la fonction de destination comme croix (x, y, a, b, c)


Est-ce que tu ayé mon nouveau code? Fonctionne-t-il comme prévu? Des bugs?



3
votes

La substitution complète est assez délicate. Voici ma tentative de le faire. Ici, nous pouvons avoir des expressions en ligne avec succès, Mais pas dans tous les scénarios. Ce code fonctionne uniquement sur AST, fait par le module ast code>. Et utilise codeGen code> pour le ramener au code. Le stringification de AST et de modification ast en général est couvert dans d'autres Q / A: "Analyser un fichier .PY, lisez l'AST, modifiez-le, puis écrivez le code source modifié" .

Nous Définissez quelques aides: P>

f1 first, unique variable names
expand call to f1(u, v) with arguments f2(x, y), x
substitute
  name u for f2(x, y)
  name v for x
  in sin((u + v))
expand call to f2(i, j) with arguments x, y
substitute
  name i for x
  name j for y
  in ((i + j) ^ 2)
((a + b) + sin((((x + y) ^ 2) + x)))
---
f1 first
expand call to f1(x, y) with arguments f2(x, y), x
substitute
  name y for x
  name x for f2(x, y)
  in sin((x + y))
expand call to f2(x, y) with arguments x, y
substitute
  name y for y
  name x for x
  in ((x + y) ^ 2)
((a + b) + sin((((x + y) ^ 2) + x)))
---
f2 first
expand call to f1(x, y) with arguments f1(x, x), y
expand call to f1(x, y) with arguments x, x
substitute
  name y for x
  name x for x
  in (x + y)
substitute
  name y for y
  name x for (x + x)
  in (x + x)
((x + x) + ((x + x) + x))
---
fact
expand call to fact(n) with arguments n
substitute
  name n for n
  in n if (n == 0) else (n * fact((n - 1)))
expand call to fact(n) with arguments (n - 1)
substitute
  name n for (n - 1)
  in n if (n == 0) else (n * fact((n - 1)))
expand call to fact(n) with arguments ((n - 1) - 1)
substitute
  name n for ((n - 1) - 1)
  in n if (n == 0) else (n * fact((n - 1)))
n if (n == 0) else (n * (n - 1) if ((n - 1) == 0) else ((n - 1) * ((n - 1) - 1) if (((n - 1) - 1) == 0) else (((n - 1) - 1) * fact((((n - 1) - 1) - 1)))))


1 commentaires

On dirait que le projet de CodeGen est homéancial et a des bugs critiques pour l'utilisation (pas de parenthèse autour de binop, donc aucune priorité d'exploitation) Le projet principal a cette demande en attente en attente d'attente github.com/andreif/codegen/pull/4/commits Donc, si quelqu'un veut l'utiliser, il serait préférable d'utiliser github.com/jbremer/codegen