2009-08-06 7 views
9

Ich weiß, dass es in einer Subroutine in Perl eine sehr gute Idee ist, die "Standardvariable" $_ mit local beizubehalten, bevor sie etwas damit macht, falls der Aufrufer sie benutzt, zB:

sub f() { 
    local $_;    # Ensure $_ is restored on dynamic scope exit 
    while (<$somefile>) { # Clobbers $_, but that's OK -- it will be restored 
     ... 
    } 
} 

Oft ist der Grund, warum Sie $_ verwenden, ersteres, weil Sie Regexes verwenden wollen, die Ergebnisse in handliche "magische" Variablen wie $1, $2 usw. bringen können. Ich möchte diese Variablen auch beibehalten, aber ich habe es nicht getan war in der Lage, einen Weg zu finden, dies zu tun.

Alles was perlvar sagt ist, dass @+ und @-, von denen $1 usw. abhängen, intern zu sein scheinen, beziehen sich auf die "letzten erfolgreichen Unterübereinstimmungen im aktuell aktiven dynamischen Bereich". Aber selbst das scheint meinen Experimenten zu widersprechen. Empirisch druckt der folgende Code "aXaa", wie ich es mir erhofft hatte:

$_ = 'a'; 
/(.)/;   # Sets $1 to 'a' 
print $1;  # Prints 'a' 
{ 
    local $_; # Preserve $_ 
    $_ = 'X'; 
    /(.)/;  # Sets $1 to 'X' 
    print $1; # Prints 'X' 
} 
print $_;  # Prints 'a' ('local' restored the earlier value of $_) 
print $1;  # Prints 'a', suggesting localising $_ does localise $1 etc. too 

Aber was ich wirklich überraschend finde, ist, dass in meiner ActivePerl 5.10.0 zumindest die local-Zeile immer noch mit $1 kommentiert - - das heißt, die Antwort "aXXa" wird produziert! Es scheint, dass der lexikalische (nicht dynamische) Umfang des geschweiften Blocks den Wert von $1 irgendwie erhält.

So finde ich diese Situation bestenfalls verwirrend und würde gerne eine definitive Erklärung hören. Wohlgemerkt, ich würde mich mit einem kugelsicheren Weg begnügen, alle Regex-bezogenen magischen Variablen zu erhalten, ohne sie alle wie folgt aufzählen zu müssen:

local @+, @-, $&, $1, $2, $3, $4, ... 

was eindeutig ein ekelhafter Hack ist. Bis dahin werde ich mir Sorgen machen, dass jeder Regex, den ich berühre, etwas durchkreuzt, was der Anrufer nicht erwartet hat.

Danke!

+0

@pilcrow: ich nicht Ihre Antwort fangen habe aber sah sie schickte mich in einer Zusammenfassung E-Mail-SO - I schätze du hast es gelöscht? Wie auch immer, ich dachte, es wäre eine nützliche Antwort, und wenn Sie das zufällig lesen: Ich suchte nach der ersten Art von Konservierung. Die zweite war tatsächlich, was ich gehofft hatte, würde * nicht * passieren - das würde auf Dinge hinauslaufen, die "lokal" in andere Bereiche "überbluten" sollten, und ja, wenn ich diese Art von "Erhaltung" wollte, würde ich einfach kopieren $ 1 usw. in andere lokale Variablen, wie Sie vorgeschlagen haben. –

Antwort

8

Vielleicht können Sie eine bessere Formulierung für die Dokumentation vorschlagen. Dynamischer Gültigkeitsbereich bedeutet alles bis zum Anfang des umschließenden Blocks oder der Unterroutine sowie alles bis zum Beginn dieses Blocks oder Unterprogrammaufrufs usw. außer, dass alle geschlossenen Blöcke ausgeschlossen sind.

Eine andere Art, es zu sagen: "Letzte erfolgreiche Unterübereinstimmungen im aktuell aktiven dynamischen Bereich" bedeutet, dass zu Beginn jedes Blocks implizit für jede Variable ein local $x=$x; steht.

Die meisten Erwähnungen des dynamischen Bereichs (zum Beispiel http://perldoc.perl.org/perlglossary.html#scope oder http://perldoc.perl.org/perlglossary.html#dynamic-scoping) nähern sich von der anderen Seite her. Sie gelten, wenn Sie an eine erfolgreiche Regex als implizit tun eine local $1, etc. denken

+4

+1. '$ 1' und so weiter sind automatisch 'lokal'() und der Bereich ist verwirrend. – pilcrow

+0

Danke, sehr hilfreich! Ja, ich war verwirrt von dem Begriff "dynamischer Bereich", den ich (aus irgendeinem Grund) zum Blockieren/Ignorieren von Blöcken verwendete. Es ist eine Erleichterung zu wissen, dass $ 1 etc. implizit lokalisiert sind. (Basierend auf Ihren Links, ich denke, es gibt nichts explizit in den Perl-Docs so?) –

3

Ich bin mir nicht sicher, ob es wirklich einen Grund gibt, über all diese Variablen paranoid zu sein. Ich habe es geschafft, Perl fast zehn Jahre lang zu benutzen, ohne dass ich in diesem Zusammenhang einmal explizit local verwenden musste.

Die Antwort auf Ihre spezifische Frage lautet: Die Anzahl der Ziffernvariablen ist nicht gegeben (obwohl die Anzahl der Übereinstimmungen, mit denen Sie arbeiten können, begrenzt ist). Es ist also nicht möglich, alle gleichzeitig zu lokalisieren.

+2

@Sinan, nicht ganz. Sie sind alle schon immer lokal(). Der Fragesteller ist dadurch und vielleicht durch die Feinheiten der beiden Paradigmen in Perl verwirrt. – pilcrow

+0

@Sinan: Anruferspeicherung lässt sich nicht gut auf große Systeme skalieren, da der Anrufer immer darauf achten muss, was alle aufgerufenen Funktionen durcheinander bringen, bis hinunter zum Anrufdiagramm. Es ist also leicht für eine Änderung in der Implementierung einer Low-Level-Funktion, mit einer High-Level-Funktion zu verfahren. Es ist in Ordnung für 100-Zeilen-Skripte, aber wenn Sie ein großes System schreiben, müssen Sie in der Lage sein, die Implementierungsdetails der Funktionen, die Sie aufrufen, nicht zu interessieren. –

+0

Aber, das ist die Sache: Ich musste mich nie um die Implementierungsdetails der Funktionen kümmern, die ich anrufe (naja, vergessen wir 'Win32 :: OLE' für einen Moment). Angesichts der Klärung von pilcrow ist es offensichtlich, warum. –

1

Ich denke, dass Sie sich zu viel Sorgen machen. Das Beste, was zu tun ist, Ihre Match-Operator laufen, sofort die Werte speichern Sie in sinnvolle Variablen wollen, dann lassen Sie die speziellen Variablen tun, was sie über sie, ohne sich Gedanken machen:

if($string =~ m/...(a.c).../) { 
    my $found = $1; 
    } 

Als ich Teile des erfassen möchten Strings, verwende ich meistens das Spiel Betreiber in Listenkontext eine Liste der Erinnerungen zurück zu bekommen:

my @array = $string =~ m/..../g; 
+0

Ich muss da nicht zustimmen :) Im Laufe der Jahre habe ich festgestellt, dass jede Codierung Praxis "unwahrscheinlich" Probleme (z. B. $ 1 etc.nicht ändern über Anrufe zu anderen Funktionen), wird schließlich Probleme verursachen * es sei denn, es gibt eine Garantie, dass es nicht * kann, so suche ich heute nach Garantien. Glücklicherweise konnte mir in diesem Fall zeigen, dass Perl eine solche Garantie bietet. –

+0

Angenommen, Perl hat $ 1 usw. nicht automatisch lokalisiert. Dann ist es einfach zu sehen, wie zerbrechlich das erste Code-Snippet sein würde: Angenommen, im Verlauf der Wartung wird diese "if" -Anweisung zu "if ($ string = ~ m /...(ac).../ && some_other_test()) {...} ". Alles funktioniert gut, bis jemand eine Regex irgendwo tief in den Eingeweiden von some_other_test() hinzufügt, zu welchem ​​Zeitpunkt $ found beginnt, seltsame Werte zugewiesen zu bekommen. Würden Sie zustimmen, dass dies ein plausibles Wartungsszenario ist? –

+0

Also geht ein Typ in den Arzt und sagt: "Es tut weh, wenn ich meinen Arm so bewege!". Der Arzt sagt: "Bewege deinen Arm nicht so!". Mein erstes Code-Snippet ist überhaupt nicht fragil. Es ist deine Veränderung, das ist das Problem. Ich habe die Operationen isoliert durchgeführt. Sie tun nicht, was ich sage: Speichern Sie sofort das Ergebnis. –

Verwandte Themen