Wie kann ich die variable Interpolation in einer Regex zur Verwendungsstelle verzögern?

8

Stellen wir uns zum Beispiel vor, dass ich eine Reihe von Variablen und ein Array von Regexes habe, die diese Variablen interpolieren:

%Vor%

Der obige Code gibt uns Warnungen, dass $var1 , $var2 und $var3 nicht zum Zeitpunkt der Regex-Kompilierung für die Regexes in $search_regexes definiert sind. Ich möchte jedoch die variable Interpolation in diesen Regexes bis zu dem Punkt verzögern, an dem sie tatsächlich verwendet werden (oder später (neu) kompiliert werden, sobald die Variablen Werte haben):

%Vor%

Wie würde ich das Konstrukt im ursprünglichen Codebeispiel umstrukturieren, um dies zu ermöglichen?

Als Bonus möchte ich jeden Regex kompilieren, nachdem der entsprechenden Variablen in dieser Regex ein Wert zugewiesen wurde, genauso wie der Operator qr// jetzt (aber zu früh). Wenn Sie zeigen können, wie Sie die Lösung weiter ausbauen können, würde ich dies sehr zu schätzen wissen.

Aktualisierung:

Ich habe mich für eine Variante des Ansatzes von Hunter entschieden, weil ich damit keinen Leistungseinbruch mache und meinen bestehenden Code minimal verändert. Andere Antworten haben mir auch einiges über alternative Lösungen für dieses Problem und deren Auswirkungen auf die Leistung bei sehr vielen Linien gelehrt. Mein Code sieht jetzt wie folgt aus:

%Vor%

Dadurch bekomme ich, was ich gesucht habe, mit minimalen Änderungen an meinem Code (d. h. nur das Hinzufügen von Subs zum Array oben) und keinen Performance-Hit pro Regex-Nutzung.

    
Michael Goldshteyn 13.02.2014, 16:32
quelle

5 Antworten

8

Ich denke, wenn Sie jeden regulären Ausdruck in anonyme Sub-Zeilen umbrechen, können Sie diese Art von Verzögerung durchführen:

%Vor%

Wenn Sie sie dann bewerten, rufen Sie einfach den anonymen Sub:

auf %Vor%

Ich weiß im Schema, dass dies thunking genannt wird. Ich bin mir nicht sicher, ob es in Perl einen Namen hat . Sie können etwas in Ruby mit Proc-Objekten

tun     
Hunter McMillen 13.02.2014, 16:48
quelle
11

Die beste Lösung wäre, die Kompilierung der Regex zu verzögern, bis diese Variablen definiert sind. Aber zuerst eine fragwürdige Lösung: Regexen können Code enthalten: qr/foo (??{ $var1 })/ . Der Block wird während der Übereinstimmung ausgeführt, und das Ergebnis des Blocks wird dann als Muster verwendet.

Wie können wir die Zusammenstellung verschieben?

  1. Indem Sie sie einfach angeben, wenn die Variablen zugewiesen wurden. Dies ist weniger ein Problem als Sie vielleicht denken, da jedes Programm ausgedrückt werden kann, ohne Variablen (wieder) zuzuweisen. Bleiben Sie bei der Regel, dass jede Deklaration auch eine Zuweisung sein muss (und umgekehrt), und das sollte funktionieren. Dies:

    %Vor%

    wird zu:

    %Vor%
  2. Wenn dies nicht möglich ist, möchten wir vielleicht eine Schlussfolgerung verwenden, die wir vor dem Abgleich auswerten:

    %Vor%

    Natürlich würde dies die Regex bei jedem Aufruf neu kompilieren.

  3. Wir können die vorherige Idee erweitern, indem wir die Regex zwischenspeichern:

    %Vor%

    Dann:

    %Vor%
amon 13.02.2014 16:50
quelle
3

(??{ }) macht genau das, wonach Sie fragen.

%Vor%

Aber das ist sehr peinlich. Die ursprüngliche Zeichenfolge ist eine so genannte Vorlage. Es gibt zahlreiche Templating-Systeme zur Verfügung, die dies sauberer machen würden.

%Vor%

Wenn die Vorlage nicht in einer Datei gespeichert werden muss, können Sie eine Builder-Untergruppe verwenden.

%Vor%

Hinweis: Innerhalb von (??{ }) können Sie Probleme mit lexikalischen Variablen haben, die auf der Außenseite deklariert sind. Deshalb habe ich im ersten Snippet eine Paketvariable verwendet.

    
ikegami 13.02.2014 18:40
quelle
2

Amons Antwort ist die vollständigste. Die Frage ist jedoch, warum sollten Sie Ihre regulären Ausdrücke vorkompilieren, wenn Sie nicht zu 100% sicher sind, was sie sein sollten?

Wie bei jeder Kompilierung muss zum Zeitpunkt der Kompilierung alles gelöst sein. Sie können, wie Ihnen gezeigt wurde, Ihren regulären Ausdruck mit Variablen angeben, aber das wird Ihren regulären Ausdruck neu kompilieren, wenn Sie sie erneut aufrufen.

Ich vermute, dass Sie sich keine Sorgen über die Kompilierungszeit als einfache Wiederverwendung machen. Wenn Sie diese regulären Ausdrücke immer und immer wieder verwenden, ist es nicht besser, nur einen einzigen Ort zu haben, an dem sie gepflegt werden?

Nun, das klingt nach dem was ein Unterprogramm ist:

%Vor%

Nun können Sie test_regex wie folgt aufrufen:

%Vor%

Sie haben einen einzigen Punkt, an dem Sie Ihre regulären Ausdrücke (in Ihrer Subroutine) pflegen müssen, aber Sie haben immer noch die Flexibilität, sie immer wieder aufzurufen. Beachten Sie, dass ich drei Parameter übergeben muss: Was ich teste (es könnte $_ sein, aber vielleicht nicht), der Wert von $var1 und die Unterprogrammnummer.

Ich konnte globale Werte in meiner Subroutine verwenden, aber das ist normalerweise eine schlechte Idee:

%Vor%

Dann wäre der Anruf:

%Vor%

Es ist mehr in der Reihe, was Sie hatten, aber es ist keine gute Idee.

    
David W. 13.02.2014 17:50
quelle
-2

Wenn dies nur Zeichenfolgen sind, versuchen Sie, sie in einfache Anführungszeichen zu schreiben:

%Vor%

Das sollte sicher verhindern, dass die Variablen interpoliert werden, wenn das Array definiert wird, aber ich kann nicht versprechen, dass sie interpolieren, wenn Sie sie tatsächlich verwenden. Versuchen Sie es trotzdem.

    
Phil Perry 13.02.2014 16:46
quelle

Tags und Links