Seite 1 von 1
Aus einer Zeichenkette eine Funktion machen
Verfasst: Montag 21. Dezember 2009, 16:11
von Crowdog
Hallo,
ich möchte mehrere längere Zeichenketten der Form
"+3.0AAACD + 5.0ACFDB - 10.0EAADF", usw.
automatisch in mathematische Funktionen von mehreren Variablen verwandeln. (Dabei wird AAA als A^3 interpretiert.)
Im obigen Fall wäre es
f(A,B,C,D,E,F) = +3.0AAACD + 5.0ACFDB - 10.0EADF
so dass man z.B.
f(2,1,1,1,1,1) = +24 + 10 - 20 = 14 bekommt. Für meine Zwecke brauche ich nur ganze Zahlen in die Funktion einzusetzen.
Ich habe zwar ein paar Ideen, wie man dies sehr umständlich machen könnte, bin aber überzeugt, dass es ganz leicht gehen müßte.
Vielen Dank im voraus!
Re: Aus einer Zeichenkette eine Funktion machen
Verfasst: Montag 21. Dezember 2009, 16:23
von numerix
Spontan fällt mir eine quick'n dirty Lösung ein, aber die werde ich lieber nicht zeigen (das gibt immer Prügel hier im Forum).
Für eine saubere Lösung wäre wichtig zu wissen was genau es heißt, wenn du schreibst "der Form". Diese "Form" müsstest du etwas allgemeiner spezifizieren, d.h. der Aufbau müsste klar definiert werden (Welche Variablen können vorkommen? Sind es immer fünf hintereinander? Steht am Anfang IMMER ein Fließkommawert mit genau einer Stelle nach dem Dezimalpunkt? Etc.)
Zur Form der Zeichenketten
Verfasst: Montag 21. Dezember 2009, 16:33
von Crowdog
Danke für die schnelle Antwort!
Die Zeichenketten bestehen aus höchstens 16 Buchstaben von A,B,C,...P.
Jede Zeichenkette fängt so "+a.0ABCFD + ... + " oder "-a.0ABCFD + ... + " mit einer Ganzzahl "a".
Schlimmstenfalls könnte sowas als Zeichenkette vorkommen:
+12.0AHIAD -10.0AEEKK +16.0LJIDG +10.0DFFDA -6.0ANNFF -4.0CBBOJ -4.0LCCBG +4.0NLLHF -10.0AHHEE -10.0KDDAK +4.0ICIOJ +10.0ECCEA +10.0BEEBA +10.0AAADD +20.0HKKNF +5.0JJJJA +2.0BAABA -40.0KDGAF -10.0AJJFF -8.0MLINA -10.0FIIAF -16.0CFIEJ +10.0AHHKK +4.0NCNNG +4.0ICING -16.0BMFGD -16.0JKOLE -2.0HCCHA +10.0LLJJA -4.0BEELG -12.0BAALG +10.0DBBAD -4.0HMMID -4.0ILIOF -8.0JFCLA -30.0DGAGD +4.0BMBNE +4.0CHHIK -4.0BMBOD +2.0NOONA
Verfasst: Montag 21. Dezember 2009, 17:02
von Ronnie
Ich würde es erstmal via Regex zerlegen:
Code: Alles auswählen
>>> text = "+3.0AAACD + 5.0ACFDB - 10.0EAADF"
>>> import re
>>> re.findall(r"\s*([+-]{1})\s*(\d+\.?\d?)(\w+)", text)
[('+', '3.0', 'AAACD'), ('+', '5.0', 'ACFDB'), ('-', '10.0', 'EAADF')]
Zweiter Schritt wäre dann ein Dictionary für die Variablen anzulegen. Dann aus dem Dict ein Dict mit Lambdas erzeugen. Zuletzt schrittweise die Tupel abarbeiten.
Verfasst: Montag 21. Dezember 2009, 17:40
von BlackJack
Mal ein quick'n'dirty Ansatz:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf-8
import operator
import re
from functools import partial
PART_RE = r'\s*([+-])\s*([0-9]+)\.0([A-P]+)'
product = partial(reduce, operator.mul)
class Function(object):
def __init__(self, parts):
self.parts = parts
def __call__(self, *args):
return sum(c * product(args[v] for v in vs) for c, vs in self.parts)
@classmethod
def from_string(cls, string):
def build_part((sign, constant, variables)):
return (int(sign + constant),
[ord(c) - ord('A') for c in variables])
return cls(map(build_part, re.findall(PART_RE, string)))
def main():
data = '+3.0AAACD + 5.0ACFDB - 10.0EADF'
func = Function.from_string(data)
print func(2, 1, 1, 1, 1, 1)
if __name__ == '__main__':
main()
Verfasst: Montag 21. Dezember 2009, 17:44
von numerix
Und 'ne Variante ohne RE:
Code: Alles auswählen
def evaluate(expr, values):
expressions = expr.replace('-','+-').lstrip('+').split('+') # geändert!
total = 0
for e in expressions:
a, b = e.strip().split('.')
val = int(a)
for v in b[1:]:
val *= values[v]
total += val
return total
expr = """+12.0AHIAD -10.0AEEKK +16.0LJIDG +10.0DFFDA -6.0ANNFF -4.0CBBOJ
-4.0LCCBG +4.0NLLHF -10.0AHHEE -10.0KDDAK +4.0ICIOJ +10.0ECCEA +10.0BEEBA
+10.0AAADD +20.0HKKNF +5.0JJJJA +2.0BAABA -40.0KDGAF -10.0AJJFF -8.0MLINA
-10.0FIIAF -16.0CFIEJ +10.0AHHKK +4.0NCNNG +4.0ICING -16.0BMFGD -16.0JKOLE
-2.0HCCHA +10.0LLJJA -4.0BEELG -12.0BAALG +10.0DBBAD -4.0HMMID -4.0ILIOF
-8.0JFCLA -30.0DGAGD +4.0BMBNE +4.0CHHIK -4.0BMBOD +2.0NOONA"""
values = dict(zip('ABCDEFGHIJKLMNOP',[1,2,-2,0,1,3,-2,2,3,4,-4,0,1,1,-1,3]))
print evaluate(expr, values)
Edit: Zeile 2 nachträglich geändert: replace() und lstrip() vertauscht.
Danke!
Verfasst: Montag 21. Dezember 2009, 17:51
von Crowdog
Ich bin mehr als zufrieden mit den Antworten... vielen Dank!
Verfasst: Montag 21. Dezember 2009, 17:55
von Ronnie
Nochmal etwas ausführlicher und etwas abweichend von meinem ursprünglischen Plan:
Code: Alles auswählen
#!/usr/bin/env python
import re
text = "+3.0AAACD + 5.0ACFDB - 10.0EAADF"
class expression:
def __init__(self, t):
self.op, self.val, self.items = t
def evaluate(self, lut):
lut['+'] = 1
lut['-'] = -1
accu = 1
accu *= lut[self.op]
accu *= int(float(self.val))
for i in self.items:
accu *= lut[i]
return accu
params = { 'A': 2, 'B': 1, 'C': 1, 'D': 1, 'E': 1, 'F': 1 }
print(
sum(
[expression(e).evaluate(params) for e in re.findall(
r"\s*([+-]{1})\s*(\d+\.?\d?)(\w+)", text
)]
)
)
Verfasst: Montag 21. Dezember 2009, 18:26
von derdon
Wozu die eckigen Klammern? Warum schreibst du `expression` klein?
Verfasst: Montag 21. Dezember 2009, 18:54
von Ronnie
derdon hat geschrieben:Wozu die eckigen Klammern? Warum schreibst du `expression` klein?
Sorry, war eine Missachtung von PEP8 - ist für "mehrsprachige" manchmal schwierig an übliche Konventionen zu denken. Die [] sind für die List-Comprehension?!
Verfasst: Montag 21. Dezember 2009, 19:13
von derdon
Code: Alles auswählen
>>> sum([i for i in xrange(4,44,4)])
220
>>> sum((i for i in xrange(4,44,4)))
220
>>> sum(i for i in xrange(4,44,4))
220
Statt einer List Comprehension kann auch eine Generator Expression übergeben werden. Wenn eine Funktion eine Generator Expression als einziges Argument übergeben bekommt, dann kann auch auf die runden Klammern verzichtet werden.
Verfasst: Montag 21. Dezember 2009, 19:37
von Ronnie
derdon hat geschrieben:Statt einer List Comprehension kann auch eine Generator Expression übergeben werden.
Wieder was dazu gelernt. Vielen Dank!
Verfasst: Montag 21. Dezember 2009, 20:04
von ms4py