Aus einer Zeichenkette eine Funktion machen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Crowdog
User
Beiträge: 3
Registriert: Montag 21. Dezember 2009, 16:00

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!
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

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.)
Crowdog
User
Beiträge: 3
Registriert: Montag 21. Dezember 2009, 16:00

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
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

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.
Achtung: User ist ein Python-Lehrling!
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()
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

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.
Zuletzt geändert von numerix am Montag 21. Dezember 2009, 19:17, insgesamt 1-mal geändert.
Crowdog
User
Beiträge: 3
Registriert: Montag 21. Dezember 2009, 16:00

Ich bin mehr als zufrieden mit den Antworten... vielen Dank!
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

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
        )]
    )
)
Achtung: User ist ein Python-Lehrling!
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Wozu die eckigen Klammern? Warum schreibst du `expression` klein?
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

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?!
Achtung: User ist ein Python-Lehrling!
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

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.
Ronnie
User
Beiträge: 73
Registriert: Sonntag 21. März 2004, 17:44

derdon hat geschrieben:Statt einer List Comprehension kann auch eine Generator Expression übergeben werden.
Wieder was dazu gelernt. Vielen Dank!
Achtung: User ist ein Python-Lehrling!
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Hier noch eine Alternative mit ast:
http://www.python-forum.de/post-149756.html#149756
Antworten