String mit Funktionsaufruf interpretieren
Ralf
- javascript
Hallo!
Ich habe folgendes Problem und hoffe, dass ich es mit Javascript überhaupt lösen kann.
Ausgangslage: Ich bekomme einen String mit Inhalt der nachfolgenden Art:
"abc [[[var1]]] def [[[var2]]] xyz"
In idesem String sind feste Bestandteile enthalten und solche, die ersetzt werden sollen. Die Ersetzung erfolgt nach folgendem Muster:
"[[[var1]]]" wird ersetzt durch get_attr("var1")
Der tatsächliche Wert soll also zum Ausführungszeitpunkt ermittelt werden. Es handelt sich um eine Anwendung, wo aus einem Datenbestand mittels des übergebenen Strings Daten extrahiert werden sollen.
Natürlich kann ich mit match() bei jedem Satz aus dem Datenbestand die Variablen ermitteln und mit dem Funktionswert ersetzen, aber gibt es nicht eine bessere Lösung?
Eigentlich brauche ich als Ergebnis "abc "+get_attr("var1")+" def "+get_attr("var2")+" xyz".
Aber es gibt ja in Javascript keine Möglichkeit, um solche Ausdrücke zu interpretieren - oder etwa doch?
Ralf
Hi,
Natürlich kann ich mit match() bei jedem Satz aus dem Datenbestand die Variablen ermitteln und mit dem Funktionswert ersetzen, aber gibt es nicht eine bessere Lösung?
sicher doch. Erweitere den String-Prototype durch eine (z.B.) .resolve()-Methode, die genau das tut, was Du eben beschrieben hast.
Aber es gibt ja in Javascript keine Möglichkeit, um solche Ausdrücke zu interpretieren - oder etwa doch?
Das Tolle an JavaScript ist, dass _Du_ bestimmst, was es kann.
Cheatah
Hi und vielen Dank für die schnelle Antwort.
sicher doch. Erweitere den String-Prototype durch eine (z.B.) .resolve()-Methode, die genau das tut, was Du eben beschrieben hast.
Von "Prototype" habe ich schon mal was gelesen. Aber bevor ich mich damit näher befasse, möchte ich sicher gehen, dass du mein Anliegen auch so verstanden hast, wie ich es gemeint habe.
Nehmen wir mal an, dass mein Datenbestand 100 Sätze hat, die ich mit dem übergebenen String "auswerten" möchte.
Mein Problem besteht nun _nicht_ darin, die Funktion get_attr() für jeden übergebenen Variablennamen 100x aufzurufen - denn das muss auf jeden Fall passieren.
Ich möchte aber nicht 100x für den übergebenen String die Namen der Variablen extrahieren und durch den Funktionsaufruf ersetzen.
Falls das genau mit deinem Vorschlag erreicht werden kann - könntest du meinem Verständnis noch ein wenig nachhelfen?
Mir ist nämlich nicht klar, an welcher Stelle ich mit der eigenen Funktion einsetzen soll.
Ralf
Falls das genau mit deinem Vorschlag erreicht werden kann - könntest du meinem Verständnis noch ein wenig nachhelfen?
Mir ist nämlich nicht klar, an welcher Stelle ich mit der eigenen Funktion einsetzen soll.
Kommt darauf an wieviel Flexibilität du haben willst.
Wenn du immer die gleiche Funktion für alle Fundstücke aufrufen willst, reicht sowas:
String.prototype.resolve= function(get_attr )
{
var new_string = this.replace( /\[\[\[([^\]]*)\]\]\]/g, get_attr ("$1") );
return new_string;
}
function get_attr(s)
{
return "*" + s + "*";
}
var string = "abc [[[var1]]] def [[[var2]]] xyz";
alert( string.resolve( ) );
Struppi.
Wenn du immer die gleiche Funktion für alle Fundstücke aufrufen willst,
Für den Anfang schon - da geht es nur um das Ersetzen der Variablen. Später soll aber der String auch noch Berechnungsfunktionen enthalten können - etwa in der Art "[[[v1]]]*([[[v2]]]+[[[v3]]])". Oder auch noch weitere Möglichkeiten.
In jedem Fall sollen zunächst die Variablen ersetzt werden.
reicht sowas:
Ich hab den Code mal unverändert übernommen und bekomme folgenden Fehler (Firefox): get_attr is not a function
Wo liegt das Problem? Ist so etwas bei replace() nicht erlaubt?
Ralf
reicht sowas:
Ich hab den Code mal unverändert übernommen und bekomme folgenden Fehler (Firefox): get_attr is not a function
Wo liegt das Problem? Ist so etwas bei replace() nicht erlaubt?
Argh, ich hatte was vergessen, nimm den Parameter aus der Funktion raus.
Struppi.
Argh, ich hatte was vergessen, nimm den Parameter aus der Funktion raus.
Klar - da hätte ich nun auch selbst drauf kommen können ...
Auf jeden Fall schon mal vielen Dank. Zwar wird auch jetzt bei _jedem_ Datensatz die Umsetzung des Namens durch den Funktionsaufruf durchgeführt, aber das lässt sich wohl nicht ändern.
Hast du vielleicht eine Idee oder einen Hinweis für mich, wie ich weiter vorgehen soll, wenn der übergebene String "mächtiger" werden soll - ich also weitere Funktionalität dem Anwender zur Verfügung stellen möchte?
Im Moment stelle ich mir das so vor, dass in dem String Funktionsaufrufe vorhanden sein können - etwa in der Art (nach der Ersetzung von Variablen): RECHNE[3*(4+12)]
Wäre dann wieder die richtige Vorgehensweise, mit replace() den Teil innerhalb der [] Klammern zu extrahieren und mit eval() zu berechnen?
Und noch weiter gedacht - könnte man das auch rekursiv aufrufen?
Falls es von Interesse ist - es handelt sich um eine Art Definitionssyntax zur Auswertung von Datensätzen, welche in Form von HTML-Tags vorliegen.
Ralf
Auf jeden Fall schon mal vielen Dank. Zwar wird auch jetzt bei _jedem_ Datensatz die Umsetzung des Namens durch den Funktionsaufruf durchgeführt, aber das lässt sich wohl nicht ändern.
Du kannst zumindest in der Funktion jeden Treffer checken.
Hast du vielleicht eine Idee oder einen Hinweis für mich, wie ich weiter vorgehen soll, wenn der übergebene String "mächtiger" werden soll - ich also weitere Funktionalität dem Anwender zur Verfügung stellen möchte?
Im Moment stelle ich mir das so vor, dass in dem String Funktionsaufrufe vorhanden sein können - etwa in der Art (nach der Ersetzung von Variablen): RECHNE[3*(4+12)]
Wäre dann wieder die richtige Vorgehensweise, mit replace() den Teil innerhalb der [] Klammern zu extrahieren und mit eval() zu berechnen?
Falls du wirklich rechnen willst, ja.
Und noch weiter gedacht - könnte man das auch rekursiv aufrufen?
Das könnte schwierig werden. Da die regulären Ausdrücke in JS nur einen begrenzten Funktionsumfang haben (wenn ich das jetzt richtig verstehe, dass evtl. Treffer [] verschachtelt sein können).
Struppi.
Du kannst zumindest in der Funktion jeden Treffer checken.
Hab jetzt nicht verstanden, was du damit ausdrücken willst. Mich stört, dass das replace() immer wieder für jeden Datensatz ausgeführt wird, obwohl sich an dem übergebenen String nichts geändert hat.
Falls du wirklich rechnen willst, ja.
Natürlich ist der Anwender für den Inhalt verantwortlich, aber das gibt mir eval() ja auch zurück.
Und noch weiter gedacht - könnte man das auch rekursiv aufrufen?
Das könnte schwierig werden. Da die regulären Ausdrücke in JS nur einen begrenzten Funktionsumfang haben (wenn ich das jetzt richtig verstehe, dass evtl. Treffer [] verschachtelt sein können).
Ganz so komplex soll es nun wieder auch nicht werden. So sollen z.B. keine Variablen benutzt werden, um den Namen einer Variablen zu erzeugen - also _nicht_: [[[a[[[xy]]]]]]
Dagegen soll schon so etwas definiert werden können:
MITTELWERT[RECHNE[[[[v1]]]*0.95]]
Genau genommen hat das mit Rekursion nichts zu tun. Es muss aber festgelegt werden, in welcher Reihenfolge innerhalb von resolve() vorgegangen werden muss. Bevor also nach MITTELWERT gesucht wird, muss zunächst RECHNE ausgewertet worden sein.
Und wegen der Syntax muss ich mir wohl auch noch was einfallen lassen.
Ralf
hi,
MITTELWERT[RECHNE[[[[v1]]]*0.95]]
Genau genommen hat das mit Rekursion nichts zu tun.
Wieso nicht?
Du willst wiederholt "Funktionen" berechnen lassen, und diese bekommen dazu Werte übergeben.
Es muss aber festgelegt werden, in welcher Reihenfolge innerhalb von resolve() vorgegangen werden muss. Bevor also nach MITTELWERT gesucht wird, muss zunächst RECHNE ausgewertet worden sein.
Und wenn dann mal die Kombination andersherum, RECHNE[MITTELWERT[...]+7.3] o.ä., auftaucht?
gruß,
wahsaga
MITTELWERT[RECHNE[[[[v1]]]*0.95]]
Genau genommen hat das mit Rekursion nichts zu tun.
Wieso nicht?
Du willst wiederholt "Funktionen" berechnen lassen, und diese bekommen dazu Werte übergeben.Es muss aber festgelegt werden, in welcher Reihenfolge innerhalb von resolve() vorgegangen werden muss. Bevor also nach MITTELWERT gesucht wird, muss zunächst RECHNE ausgewertet worden sein.
Und wenn dann mal die Kombination andersherum, RECHNE[MITTELWERT[...]+7.3] o.ä., auftaucht?
»»
Wenn man das so allgemein halten will, hast du natürlich Recht. Das habe ich aber bisher nicht beabsichtigt. Die ganze Sache befindet sich aktuell in der Entwurfsphase. Wichtig war am Anfang für mich nur die Sache mit der Ersetzung der Variablen durch akuelle Inhalte des jeweiligen Datensatzes (bzw. DOM-Objektes).
Da die extrahierten Daten auch Zahlen beinhalten, ist es nicht abwegig, damit auch Berechnungen anstellen zu wollen (es handelt sich um Datensätze aus einer Verkaufsabwicklung).
Ich kann noch nicht abschätzen, was da mal alles an Anforderungen kommt und daher ist es schwierig, jetzt schon alle Eventualitäten berücksichtigen zu wollen.
Wie schon Cheatah geschrieben hat, wäre wohl ein Parser mit Ausgabe einer entsprechenden Objektstruktur keine schlechte Idee. Damit kenne ich mich jedoch überhaupt nicht aus und muss mangels Zeit auch davon Abstand nehmen.
Um auf das Beispiel zurückzukommen - wenn ich definiere, dass RECHNE vor MITTELWERT ausgewertet wird, dann ist das eben so und ich brauche keine Rekursion. Auf den String wird zunächst die Ersetzung der Veriablen durch die jeweiligen Inhalte durchgeführt, dann wird geRECHNEt und dann werden ggf. weitere Funktionen ausgewertet (MITTELWERT ist von mir bisher nur ein Gedankenkonstrukt - grundsätzlich sind aber schon statistische Funktionen denkbar).
Für mich ist im Moment wichtig, dass es möglichst bald läuft und benutzbar ist. Wenn dann Erweiterungswünsche kommen, muss ich sehen, wie und ob die in das vorliegende Design eingebunden werden können.
So ist das nun einmal, wenn es kein Pflichtenheft gibt ;)
Ralf
Hi,
Wie schon Cheatah geschrieben hat, wäre wohl ein Parser mit Ausgabe einer entsprechenden Objektstruktur keine schlechte Idee. Damit kenne ich mich jedoch überhaupt nicht aus und muss mangels Zeit auch davon Abstand nehmen.
nun, lass es mich so ausdrücken: Du bist definitiv in der Lage, Fragen zu stellen und mit den Antworten umzugehen. Insofern erkenne ich in Deiner Äußerung kein unlösbares Problem ;-)
Um auf das Beispiel zurückzukommen - wenn ich definiere, dass RECHNE vor MITTELWERT ausgewertet wird, dann ist das eben so und ich brauche keine Rekursion.
Was ist dann der Wert in "RECHNE[MITTELWERT[...]]"?
Für mich ist im Moment wichtig, dass es möglichst bald läuft und benutzbar ist. Wenn dann Erweiterungswünsche kommen, muss ich sehen, wie und ob die in das vorliegende Design eingebunden werden können.
So ist das nun einmal, wenn es kein Pflichtenheft gibt ;)
In dem Fall erstelle selbst eines: Schreibe die Dir bekannten Anforderungen nieder und lasse sie Dir abnicken. Das hat zur Folge, dass *keine anderen Anforderungen existieren*, aber auch, dass die Lösung auftretender Unstimmigkeiten bei Dir liegt. Ist der Auftraggeber mit dem Ergebnis nicht zufrieden, so zeige, dass die Anforderungen erfüllt wurden. Kannst Du darlegen, dass die von Dir gefundenen Lösungen sinnvoll sind, hast Du somit Deine Pflicht erfüllt. Will der Auftraggeber etwas anderes, so muss er dies klar (und in Rücksprache mit Dir - Stichworte Machbarkeit und Aufwand) definieren. Ganz nebenbei wird er dabei nach und nach die Wichtigkeit eines ordentlichen Konzepts erkennen ...
Weiterer Vorteil: Jede nachträgliche Weiterentwicklung kann *selbstverständlich* eine Neuentwicklung bedeuten. Ich empfehle Dir allerdings, dies bereits im Pflichtenheft deutlich zu machen.
Cheatah
nun, lass es mich so ausdrücken: Du bist definitiv in der Lage, Fragen zu stellen und mit den Antworten umzugehen. Insofern erkenne ich in Deiner Äußerung kein unlösbares Problem ;-)
Natürlich nicht - es ist letztlich nur eine Frage der Zeit.
Was ist dann der Wert in "RECHNE[MITTELWERT[...]]"?
Das hängt von der Fehlerbehandlung ab, die beim eval() erfolgen sollte. Oder natürlich von der Prüfung, die vor dem eval() durchgeführt wird.
So ist das nun einmal, wenn es kein Pflichtenheft gibt ;)
In dem Fall erstelle selbst eines: Schreibe die Dir bekannten Anforderungen nieder und lasse sie Dir abnicken. Das hat zur Folge, dass *keine anderen Anforderungen existieren*, aber auch, dass die Lösung auftretender Unstimmigkeiten bei Dir liegt. Ist der Auftraggeber mit dem Ergebnis nicht zufrieden, so zeige, dass die Anforderungen erfüllt wurden. Kannst Du darlegen, dass die von Dir gefundenen Lösungen sinnvoll sind, hast Du somit Deine Pflicht erfüllt. Will der Auftraggeber etwas anderes, so muss er dies klar (und in Rücksprache mit Dir - Stichworte Machbarkeit und Aufwand) definieren. Ganz nebenbei wird er dabei nach und nach die Wichtigkeit eines ordentlichen Konzepts erkennen ...
Weiterer Vorteil: Jede nachträgliche Weiterentwicklung kann *selbstverständlich* eine Neuentwicklung bedeuten. Ich empfehle Dir allerdings, dies bereits im Pflichtenheft deutlich zu machen.
Das mit dem Pflichtenheft war eher darauf bezogen, dass es weder einen Auftraggeber noch externe Anforderungen gibt. Ich erstelle etwas, von dem ich in gewissen Teilen weiss, dass es einen Bedarf gibt und zu weiteren Teilen davon ausgehe.
Es wird also erst durch das Feedback der ersten Anwender bestimmt, wie es weiter geht. Wenn der Bedarf größer ist, als von mir eingeschätzt - wunderbar. Wenn nicht, will ich nicht zu viel Zeit damit verbraten haben, mir Gedanken über Sachen zu machen, die ich dann letztlich nicht benötige.
Auf jeden Fall wird das Projekt mein Wissen erweitern und damit ist es schon mal nicht vergebens.
Ralf
Hi,
Wäre dann wieder die richtige Vorgehensweise, mit replace() den Teil innerhalb der [] Klammern zu extrahieren und mit eval() zu berechnen?
Falls du wirklich rechnen willst, ja.
oh, so pauschal würde ich das "ja" nicht stehen lassen wollen.
Da die regulären Ausdrücke in JS nur einen begrenzten Funktionsumfang haben
Ich wüsste spontan nicht, welche Begrenzung die RegExp-Implementierung von JavaScript gegenüber denen anderer Sprachen hätte. Vielmehr ist das Problem ...
(wenn ich das jetzt richtig verstehe, dass evtl. Treffer [] verschachtelt sein können).
... dass es sich hierbei nicht um ein Muster handelt, sondern um eine Struktur. RegExp können also höchstens als Hilfsmittel dienen, niemals aber alleinige Lösung sein. Hier lohnt sich eher ein Parser, der ähnlich wie SAX vorgeht. Das Ergebnis könnte eine simple Objektstruktur sein, mit der man sich nebenbei noch das eval() spart.
Cheatah