| perlglobtut Dokumentation | Download als POD | Wie kann ich hier etwas ändern? |
my und our und des Befehls local ist in perlscopetut zu finden.
HINWEIS: Dieser Text enthält Fußnoten, die wie folgt gekennzeichnet sind: L<[1]|/item__5b1_5d>. Mit Browsern, die ein Navigieren im Text mittels Verweise unterstützen, kann man direkt durch Klicken auf die Zahl zur Fußnote gelangen; dort findet sich dann ein Verweis, der zum Haupttext zurückführt.
local Befehl ermöglicht das temporäre Wegsichern und Ersetzen eines Variablenwertes (der automatisch zu einem späteren Zeitpunkt wieder hergestellt wird), aber die Variable selbst bleibt dieselbe und kann jederzeit von überall angesprochen werden.
hugo bereitstellt, lautet somit %hugo:: (beachte das führende "%", es handelt sich ja um einen Hash). Der Name des Stashes jenes Packages, das von Perl verwendet wird, wenn nichts anderes ausgewählt wurde ( Default Package ), lautet %main:: .
Die package Direktive definiert zum Kompilierungs-Zeitpunkt ein neues Package. Sie sorgt dafür, dass der zugehörige Stash angelegt wird, falls er noch nicht existiert, und dass ab dann alle neuen Symbole, die nicht voll qualifiziert (d.h., mit expliziter Angabe eines Packagenamens versehen) oder ausdrücklich als lexikalisch gekennzeichnet sind, in den Stash als neue Elemente aufgenommen werden L<[2]|/item__5b2_5d>.
Zur Laufzeit weiß dann der Interpreter, in welchem Package nach den jeweiligen Variablen Ausschau zu halten ist L<[3]|/item__5b3_5d>.
Genauso, wie ein Hash andere Hashes (in Form von Referenzen) enthalten kann, so kann ein Stash weitere Stashes enthalten, wenn - vereinfacht ausgedrückt - in seinen Elementen keine Symbole, sondern wiederum Stashes abgelegt sind. Dadurch lassen sich Packages in der bekannten Weise
Alpha::Beta::Gammaverschachteln. Dennoch bleibt jeder Stash eigenständig und Variablen innerhalb der Stashes stehen in keinerlei Beziehung zueinander. Die Stashes selbst sind miteinander derart verbunden, dass der übergeordnete Stash einen Verweis auf den untergeordneten enthält (in obigem Beispiel enthält also
%Alpha:: ein Element, das auf %Beta:: zeigt, und dieser Stash wiederum ein Element auf %Gamma:: ). Damit wird auch das rekursive Durchsuchen von Symboltabellen möglich (siehe das Beispiel im Abschnitt Bearbeiten von Symboltabellen?).
test im Package hugo :
$hugo::{test}
Beachte, dass die Doppelpunkte hier nicht als Trenner zwischen Package- und Variablennamen verwendet werden; sie sind vielmehr Teil des Namens des Stashes %hugo:: . Im Unterschied dazu steht
$hugo{test}
für das Element test des "normalen" Hashes %hugo .
Symbolelemente im Default Package %main:: werden genauso angesprochen, so wird das Element test mit
$main::{test}
bezeichnet.
Beachte, dass die Doppelpunkte am Namensende nur für Hashes eine besondere Bedeutung haben, sie werden hierdurch als Stash gekennzeichnet. Für alle anderen Wertetypen von Perl sind sie bedeutungslos; der Skalar $hallo:: und das Array @hallo:: unterscheiden sich durch nichts von anderen Variablen (außer vielleicht durch den auffälligen Namen). Die Doppelpunkte sind aber dennoch Teil des Namens und daher sind $hallo:: und $hallo zwei unterschiedliche Variable.
Perl versteckt den Zugriff auf Stashes und deren Elemente nicht, sondern behandelt sie wie jeden anderen Hash, d.h., Hash-Operatoren wie each , exists , keys , values und delete können wie gewohnt verwendet werden, wodurch das Durchsuchen und Bearbeiten von Symboltabellen aus Perl-Code heraus möglich wird. Hiervon machen auch etliche Module Gebrauch (Beispiele dazu finden sich im Abschnitt Bearbeiten von Symboltabellen?).
Dieser Ansatz birgt natürlich auch gewisse Gefahren - so sollte man sich darüber im Klaren sein, dass in einem Skript nach Ausführen von
undef %main::;oder
%main:: = (ALLES => 'WEG');dessen Laufruhe möglicherweise etwas leiden wird. Auch das Entfernen von Stashelementen mit
delete sollte mit Bedacht geschehen. Generell ist beim modifizierenden Zugriff auf Stashes durchaus Vorsicht angebracht; Perl ist wie immer mit seinen Möglichkeiten freizügig, erwartet aber auch hier, dass der Programmierer weiß, was er tut.
test , eine Funktion test und sogar ein IO Handle test gleichzeitig nebeneinander existieren, ohne sich im geringsten zu beeinflussen.
Bei globalen Variablen wird dies durch eine Komponente namens Typeglob ermöglicht. Darunter kann man sich eine Datenstruktur mit sechs Feldern ( Slots ) vorstellen, die Platz für alle Wertetypen vorsieht, die Perl kennt:
$hugo::test vereinfacht gesagt folgendes:
Im Stash %hugo:: wird nach dem Element test gesucht. Der Wert des Elements ist in ein Typeglob L<[6]|/item__5b6_5d>.
Da dem Namen ein $-Zeichen vorangestellt ist, wird auf den Skalar-Slot des Typeglobs zugegriffen.
name() als häufigere (und verständlichere) Alternative zu &name .
Perl erlaubt das Ansprechen von Typeglobs auch direkt mit dem *-Sigil, und somit sprechen die folgenden Ausdrücke denselben Typeglob (im Package main ) an:
$main::{hugo} *hugo
Ersterer wird allerdings erst zur Laufzeit ausgewertet (da ja auf ein Hashelement, dessen Inhalt beim Kompilieren noch nicht bekannt ist, zugegriffen wird), letzterer schon beim Kompilieren, da hier der Typeglob direkt adressiert werden kann. Daher ist die zweite Notation schneller, aber weniger flexibel.
$x = 3; *x = \do {my $x = 3}; ${*x} = 3;
@y = (1, 2); *y = [1, 2]; @{*y} = (1, 2);
%z = (s => 3); *z = {s => 3}; %{*z} = (s => 3);
sub a {...}; *a = sub {...};
Der Code in den drei Spalten ist von der Wirkung her identisch, wenngleich die linke Schreibweise die Geläufigste ist (links unten findet sich eine bereits beim Kompilieren verarbeitete Deklaration ; die Zuweisungen finden hingegen erst zur Laufzeit statt). In einigen, später gezeigten Fällen führt allerdings an der Zuweisung über den Typeglob kein Weg vorbei.
Die rechte Spalte zeigt die Zuweisung ebenfalls über den Typeglob, wobei hier durch temporäre Dereferenzierung wiederum Werte und keine Referenzen zugewiesen werden. Da es in Perl keine Funktionsliterale gibt (sondern nur Referenzen darauf), ist die Zuweisung einer Funktion auf die in dieser Tabelle gezeigte Art nicht möglich.
Beachte die Verwendung von \do {...} für die Zuweisung einer anonymen Skalarreferenz, für die Perl keine eigene Syntax vorsieht. Achtung: die Verwendung von \3 ergäbe eine Referenz auf einen schreibgeschützten und daher im Vergleich zur linken Spalte nicht identischen Wert.
Besonderes Augenmerk verdient das Konstrukt in der letzten Zeile - es zeigt den Einsatz von sub in einer Deklaration (die vom Compiler ausgewertet wird) in der linken Spalte, und den Einsatz als Operator mit einer zur Laufzeit durchgeführten Zuweisung in der mittleren Spalte. In letzterem Fall liefert sub eine Referenz auf eine anonyme Subroutine, die durch Zuweisung an einen Typeglob einen Eintrag in der Symboltabelle erhält und damit ihre Anonymität verliert. Tatsächlich kann man eine vom Compiler verarbeitete Deklaration
sub mysub {
...
}
genauso gut auch als
BEGIN {
*mysub = sub { ... };
}
schreiben (allerdings ist erstere klarerweise schneller, weil sie direkt vom Compiler abgehandelt wird und kein Perl-Interpreter dafür gestartet werden muss) L<[7]|/item__5b7_5d>. Diese Technik - das Generieren von Funktionen zur Laufzeit durch Zuweisung von Codereferenzen an Typeglobs - ist recht beliebt und wird z.B. von Wrapper Funktionen? verwendet.
Beachte, dass bei den obigen Globzuweisungen auf der linken Seite stets dieselbe Angabe - eben der Typeglob - zu finden ist. Perl erkennt selbst anhand des zuzuweisenden Referenztyps , welcher Slot des Typeglobs durch die Zuweisung zu belegen ist.
Ein weiterer Punkt soll nicht unerwähnt bleiben: die Zuweisung eines Typeglobs an einen anderen Typeglob in der Form
*alpha = *beta;Sie bewirkt, dass beide Typeglobs nun dieselben Slots gemeinsam verwenden. Wird daher an
$alpha ein Wert zugewiesen, so kann dieser nun auch mit $beta angesprochen werden. Das gilt sinngemäß auch für @beta , %beta , &beta und die Handles beta . Diese Technik bezeichnet man als Aliasing und sie bildet die Basis für Module wie Exporter, die dadurch Symbole (meistens Funktionsnamen) eines Packages in einem anderen Package abbilden und es so ermöglichen, fremde (importierte) Funktion wie lokale Funktionen ohne Package-Prefix anzusprechen (siehe hierzu auch den Abschnitt über Aliasing und Importing?).
Wird versucht, an einen Typeglob etwas anderes als eine Referenz zuzuweisen:
*hugo = 'test';so nimmt Perl an, dass eine Alias-Zuweisung stattfinden soll und macht daraus
*hugo = *test;Da dies erst zur Laufzeit passiert (der Compiler generiert ungeniert den Code für die Zuweisung des Stringliterals an den Typeglob), gibt es in diesem Fall auch mit
use warnings keinerlei Hinweise auf dieses nicht ganz saubere Konstrukt.
Da man Typeglobs nicht mit our deklarieren kann, darf man sie auch mit use strict 'vars' unqualifiziert, d.h., ohne Packagenamen ansprechen L<[8]|/item__5b8_5d>.
Abschliessend sei noch kurz der Befehl
undef *hugo;erwähnt, der alle Slots des Typeglobs löscht, also einem
undef $hugo; undef @hugo; undef %hugo; ...entspricht. Hier kann man sich das "*" tatsächlich als Wildcard ("Lösche alles mit dem Namen
hugo ") vorstellen.
Es ist zu beachten, dass das Löschen eines Stashelements nicht dieselbe Wirkung hat wie das undef -Setzen eines Typeglobs. Wird z.B. mit
delete $main::{hugo};
das Stashelement gelöscht, so wird ein darauf folgender Zugriff
print $hugo;dennoch funktionieren, da die Umsetzung des Elementkeys in eine Typeglob-Adresse in der
print -Anweisung bereits beim Kompilieren geschieht. Daher wird der Stash zur Laufzeit hierfür nicht mehr benötigt und das Löschen bleibt wirkungslos. Es wird in diesem Fall auch nur das Stashelement gelöscht, nicht aber der in ihm enthaltene Typeglob.
Geschieht ein derartiges Löschen allerdings in einem BEGIN -Block, dann kann es zu einem Fehlverhalten oder Abbrechen des Skripts kommen. Ein modifizierender Zugriff auf einen Stash oder Stashelemente in BEGIN -Blöcken sollte daher, wie bereits erwähnt, mit Bedacht erfolgen.
Da an Typeglobs nur Referenzen zugewiesen werden können, ist die Zuweisung von undef
*hugo = undef;unzulässig und wird mit einem
Undefined value assigned to typeglobkommentiert, ansonsten aber ignoriert. Erlaubt ist hingegen
*hugo = \undef;und es bleibt dem Leser als Aufgabe überlassen, sich zu überlegen, was hier geschieht (oder in der Fußnote L<[9]|/item__5b9_5d> nachzusehen).
$ref = *glob;nicht mehr eindeutig, da hieraus nicht hervorgeht, welche Referenz (d.h., welcher Slot) angesprochen werden soll. Perl bietet stattdessen eine hash-ähnliche Syntax zum Ansprechen der sechs Slots eines Typeglobs an:
$scalarref = *glob{SCALAR};
$arrayref = *glob{ARRAY};
$hashref = *glob{HASH};
$coderef = *glob{CODE};
$handleref = *glob{IO};
$formatref = *glob{FORMAT};
Alle diese Zuweisungen liefern Referenzen des jeweiligen Typs zurück, die dann ganz normal dereferenziert werden können. So kann man mit
@{$arrayref}
auf das Array, dessen Referenz dem Array-Slot des Typeglobs entnommen wurden, zugreifen. Anders gesagt, sind die beiden folgenden Ausdrücke identisch:
\@hugo *hugo{ARRAY}
und daher auch diese beiden:
@hugo @{*hugo{ARRAY}}
wobei die linke Schreibweise sicher kürzer und geläufiger ist, die rechte aber die internen Abläufe beim Zugriff veranschaulicht.
Ist der angegebene Slot des Typeglobs nicht in Verwendung, wird undef , im Falle von SCALAR eine Referenz auf undef zurückgeliefert (dieser Sonderfall muss daher beim Testen ggf. berücksichtigt werden; siehe dazu das erste Beispiel im Abschnitt Bearbeiten von Symboltabellen?).
Die nahe Verwandtschaft von Typeglobs und Referenzen führt dazu, dass an Stellen, an denen eine Referenz erwartet wird, auch ein Typeglob verwendet werden kann (aber nicht immer auch umgekehrt!).
Erwähnenswert sind auch noch die folgenden Konstrukte:
$globref = *glob{GLOB};
$name = *glob{NAME};
$package = *glob{PACKAGE};
Sie liefern eine Referenz auf den Typeglob selber, sowie den Symbolnamen (d.h., den Keynamen des Stashelements, das den Typeglob enthält), und den Packagenamen (Stashnamen) zurück. Auf den ersten Blick nicht sonderlich interessant, werden diese Konstrukte wichtig, wenn man Typeglobs in Variablen als Argumente an Funktionen übergibt. Die Funktion kann dann bei Bedarf Package- und Symbolnamen des übergebenen Typeglobs ermitteln und über die Referenz wiederum direkt darauf zugreifen, was im Umgang mit Datei- und Formathandles wichtig werden kann (siehe dazu auch Abschnitt Verwendung von Stashes und Typeglobs?).
Beachte, dass diese Konstrukte nur auf der rechten Seite einer Zuweisung (d.h. als Quelle) vorkommen dürfen, auf der linken Seite akzeptiert sie der Parser nicht. Das ist auch nicht notwendig, da, wie im Abschnitt Zuweisungen an Typeglobs? gezeigt, Perl dann anhand des Referenztyps selbst erkennt, welcher Slot des Typeglobs zu belegen ist.
Beachte auch, dass es sich trotz der hash-ähnlichen Syntax nicht um Hashes und Hashkeys handelt und dass daher abgesehen von den gezeigten Beispielen keine anderen Operationen zulässig sind. Die Keynamen sind fix vorgegeben, andere Werte (z.B. *glob{XXX} ) liefern den Wert undef zurück.
Ein Typeglob selbst wird von Perl beim Zuweisen wie ein Skalar betrachtet und daher ist die Anweisung
$glob = *hugo; print $glob; # Gibt "*main::hugo" aus (im Package main)durchaus zulässig. Hier enthält der Skalar
$glob nun den Typeglob *hugo und man kann mit
*{$glob}
wiederum auf den Typeglob oder z.B. mit
$slot = 'HASH';
my %hash = %{*{$glob}{$slot}};
über den Typeglob auf den ursprünglichen Hash (hier also %hugo ) zugreifen. Da man Typeglobs in Skalaren abspeichern kann, kann man sie natürlich auch in Hash- oder Arrayelementen ablegen und so bei Bedarf an eine Funktion z.B. ein Typeglob-Array übergeben.
Es ist allerdings wichtig zu beachten, dass solcherart abgespeicherte Typeglobs nur sog. Fake-Kopien sind, was vereinfacht gesagt bedeutet, dass man auf sie bzw. ihre Slots nur mehr lesend zugreifen darf. Der Versuch, einen Slot durch Zuweisung einer Referenz zu beschreiben, bewirkt, dass der Skalar statt dem Typeglob nur mehr die angegebene Referenz entählt:
$value = *glob; # Erzeugt in $value ein Fake-Kopie von *glob
print $value; # Gibt "*main::glob" aus
*{$value} = [1,2,3]; # Array-Slot soll ueberschrieben werden
print $value; # Gibt "ARRAY(0x<Adresse>)" (Arrayreferenz) aus
Der ursprüngliche Typeglob (hier *glob ) bleibt davon unbeeinflusst.
Möchte man auch das Modifizieren von Typeglobs, die in Skalaren enthalten sind, ermöglichen, wird man Globreferenzen (siehe nächster Absatz) verwenden. Man kann aber auch mit
*newglob = $glob;den Typeglob duplizieren und dann nicht mehr über den Skalar, sondern kürzer über den neuen Typeglob zugreifen. Das ist identisch mit der weiter oben beschriebenen Methode des Aliasing ; d.h., beide Typeglobs greifen wieder auf dieselben Slots zu, die dann natürlich auch wieder modifiziert werden können. Man kann auch Typeglobs selber über Referenzen ansprechen, wenn man dem Typeglobnamen ein
\ voranstellt oder die weiter oben erwähnte Hash-Notation verwendet:
$globref = \*glob;
$globref = *glob{GLOB};
liefert beides eine Referenz auf den Typeglob zurück, wodurch das Anlegen von Fake-Kopien vermieden wird. Das kann außerdem für Funktionen, die unterschiedliche Referenzentypen entgegennehmen, hilfreich sein, da man dann mit der ref Funktion auch prüfen kann, ob eine Globreferenz übergeben wurde:
sub globderef {
my $arg = shift;
die 'Not a glob reference' unless ref $arg eq 'GLOB';
print 'Got glob reference to ', *{$arg}, "\n";
}
Hier ist es wichtig, vor der Dereferenzierung des Typeglobs sicherzustellen, dass eine solcher als Argument mitgegeben wurde, was mit ref einfach geschehen kann. Würde der Typeglob direkt übergeben, wäre eine solche Prüfung nicht so direkt möglich.
Man kann eine solche Funktion auch mit einem Prototyp * deklarieren, hier also
sub deref(*) {
...
}
dann erhält sie immer eine Globreferenz übergeben, egal ob sie nun mit einem Typeglob oder einer Referenz darauf aufgerufen wurde.
Wie hier auch zu erkennen ist, ist die Syntax zum Dereferenzieren einer Globreferenz und zum Auslesen eines Skalars, dem ein Typeglob zugewiesen wurde, diesselbe:
$globvar = *glob; # Skalar enthaelt Fake-Kopie
$globref = \*glob; # Skalar enthaelt Globreferenz
*newglob = *{$globvar}; # oder
*newglob = *{$globref};
Sauberer ist aber der Ansatz mit der Globreferenz, weil dadurch das Anlegen einer Fake-Kopie vermieden wird.
\ und den Dereferenzierungsoperator -> dort noch nicht gab. Dabei machte man sich die enge Verwandtschaft von Typeglobs und Referenzen zunutze. Um z.B. einer Funktion Zugriff auf ein Array zu ermöglichen, konnte man Folgendes schreiben:
sub my_func {
*array = shift;
print join ',',@array; # Gibt "1,2,3" aus
@array = (2,4,6);
}
@val = (1,2,3);
my_func(*val);
print join ',',@val; # Gibt "2,4,6" aus
Hier wird über den Typeglob tatsächlich ein Zugriff auf das Array (und nicht etwa eine Kopie) gestattet, wodurch Änderungen am Array auch außerhalb der Funktion bestehen bleiben.
Diese Art der Kodierung wurde in Perl 5 durch die Einführung von Referenzen überflüssig und sollte nicht mehr verwendet werden. Es ist aber möglich, dass man in älterem Perl-Code noch darauf stößt, so dass es kein Fehler ist, darüber Bescheid zu wissen.
V und X Befehlen Gebrauch.
Das folgende Beispiel zeigt, wie Stashes rekursiv durchsucht werden. Dabei macht es sich das in Abschnitt Stashes und Packages? beschriebene Konzept verschachtelter Packages zunutze (die Zeilennummern dienen nur zum Referenzieren und sind kein Bestandteil des Codes):
1 use warnings;
2 use strict;
3 my @slots = qw[SCALAR ARRAY HASH CODE IO FORMAT];
4 my $stash = shift || 'main::';
5 $stash = 'main::' if $stash eq '::';
6 $stash .= '::' unless $stash =~ /::$/;
7 my $recflg = shift;
8 show_globs($stash,$recflg);
9 sub show_globs {
10 my ($stash,$recflg,$index) = @_;
11 $index ||= 0;
12 no strict 'refs';
13 foreach my $glob (values %{$stash}) {
14 my $name = *{$glob}{NAME};
15 next if $stash eq 'main::' && $name eq 'main::';
16 my $fullname = $stash . $name;
17 foreach my $slot (@slots) {
18 my $text = ' ' x $index . '*' . $fullname . "{$slot}\n";
19 if ($slot eq 'SCALAR') {
20 print $text if defined ${$glob};
21 }
22 else {
23 print $text if defined *{$glob}{$slot};
24 }
25 }
26 show_globs($fullname,1,$index+1) if $name =~ /::$/ && $recflg;
27 }
28 }
Dieser Code listet vom angegebenen Stash (oder von %main:: , falls keine Angabe erfolgt ist) alle Typeglobs und die in ihnen belegten Slots in der Notation *package::name{slot} auf. Falls das zweite Argument auf true gesetzt ist, werden auch alle Stashes unterhalb des angegebenen rekursiv durchlaufen. An diesem Code ist einiges bemerkenswert.
Zunächst werden in Zeile 3 die bekannten Wertetypen von Perl, die in Slots von Typeglobs vorkommen können, aufgelistet. Wenn man nicht an allen Wertetypen interessiert ist, kann man die nicht Benötigten auch weglassen.
Weiters fällt die no strict 'refs' Anweisung innerhalb der Funktion in Zeile 12 auf; sie ist notwendig, da in Zeile 13 eine symbolische Referenz zum Ansprechen des jeweiligen Stashes ( %{$stash} ) verwendet wird. Dasselbe Problem stellt sich aber auch, wenn auf diese Art Typeglobs angesprochen werden sollen:
my $name = 'ENV';
print %{*{$name}{HASH}}; # dasselbe wie print %ENV
Dieser Code liefert mit use strict 'refs' den Laufzeitfehler
Can't use string ("ENV") as a symbol ref while "strict refs" in use
zurück. Wie weiter unten noch gezeigt werden wird, kann es im Umgang mit Symboltabellen und Stashes häufig notwendig werden, strict 'refs' temporär abzuschalten.
Erwähnenswert ist auch Zeile 15 - sie verhindert einen Endloslauf des Skripts. Die Implementierung von Stashes, besonders der Algorithmus zur deren Verschachtelung sieht vor, dass jeder Stash einem Elternstash entstammt. Wem entstammt dann aber %main:: ? Nun, er stammt von sich selbst ab. Anders gesagt, enthält %main:: ein Element, dessen Name main lautet und das wiederum auf %main:: zeigt L<[10]|/item__5b10_5d>. Dadurch entsteht eine (gewollte) Zirkularität, die - in diesem Beispiel - in Zeile 15 unterbrochen wird. Wenn man also in Skripts Stashes auf diese Weise durchläuft und auch %main:: verarbeiten will, muss man dessen besondere Eigenschaft ( selbstreferenzierender Stash ) in der gezeigten oder in einer ähnlichen Weise berücksichtigen.
Die Prüfung beider Variable ist notwendig, da ansonsten Stashes wie z.B. MyPackage::main:: unberücksichtigt bleiben würden.
Schließlich soll noch auf die unterschiedliche Behandlung von Skalarslots und anderen Slots in Zeile 19 ff. hingewiesen werden. Wie bereits erwähnt, ist der Skalarslot eines Typeglobs immer belegt (auch wenn der Skalarwert nur undef ist). Daher muss die im Slot abgelegte Referenz dereferienziert werden, um feststellen zu können, ob tatsächlich ein Wert vorhanden ist. Bei allen anderen Wertetypen genügt das Testen des Slots selbst.
Auf die Ausgabe der einzelnen Werte der Variablen wurde hier verzichtet, da dies im Falle von Codewerten und Handles ohnehin nicht möglich ist und die Ausgabe bei Array- und Hashwerten in Abhängigkeit von deren Elementanzahl sehr umfangreich werden kann. Auch ist nicht gesagt, dass jeder Wert nur darstellbare Zeichen enthält, so dass diese u.U. vor der Ausgabe noch entsprechend bearbeitet werden müssten. Dies sei dem Leser als Hausaufgabe überlassen.
Da übrigens auch die Namen einzelner Perl-Variable teilweise mit nicht darstellenbaren Zeichen abgespeichert werden (so wird z.B. das ^U der Variable ${^UNICODE} mit seinem tatsächlichen ASCII-Wert 0x15 (octal 025) abgespeichert), erhält man beim Starten des obigen Skripts manchmal gar keine oder unvollständige Namen (z.B. nur NICODE ). Ob und wie solche Namen dargestellt werden, hängt von den Fähigkeiten des jeweiligen Ausgabegerätes ab. Guter Code stellt daher vor der Ausgabe von Stashnamen sicher, dass diese nur darstellbare Zeichen enthalten.
Ein weiteres Beispiel, das das Löschen von Symboltabellen zeigt, ist (in etwas gekürzter Form) dem Modul Symbol.pm entnommen:
1 sub delete_package {
2 my $pkg = shift;
3 $pkg = "main::$pkg" unless $pkg =~ /^main::/;
4 $pkg .= '::' unless $pkg =~ /::$/;
5 my ($stem,$leaf) = $pkg =~ /(.*::)(\w+::)$/;
6 my $stem_symtab = *{$stem}{HASH};
7 return unless exists $stem_symtab->{$leaf};
8 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
9 foreach my $name (keys %$leaf_symtab) {
10 undef *{$pkg . $name};
11 }
12 %$leaf_symtab = ();
13 delete $stem_symtab->{$leaf};
14 }
Dieses Beispiel zeigt das Ansprechen von Stashes über Hashreferenzen, die direkt den jeweiligen Slots der Typeglobs entnommen werden (Zeilen 6 und 8). Der Code verdient eine nähere Betrachtung:
my $stem_symtab = *{$stem}{HASH};
Hier wird der Hashslot der Stashes, dessen Name in $stem abgelegt ist, geholt; das Ergebnis in $stem_symtab ist eine Hashreferenz. In Zeile 7 wird mittels Dereferenzierung geprüft, ob das angegebene Element (d.h., das in diesem Stash enthaltene Package) existiert:
return unless exists $stem_symtab->{$leaf};
Falls nicht, wird an dieser Stelle abgebrochen (was nicht existiert, braucht auch nicht gelöscht zu werden). Ansonsten wird nun der Stash, auf den dieses Element zeigt, geholt:
my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
Schreibt man das ohne die Zwischenvariable $stem_symtab , so ergibt sich:
my $leaf_symtab = *{*{$stem}{HASH}->{$leaf}}{HASH};
Das läßt sich - von innen nach außen - wie folgt auflösen:
*{$stem} liefert den Typeglob des Stashes, dessen Name in $stem steht.
*{$stem}{HASH} spricht den Hash-Slot dieses Typeglobs an, das ergibt daher eine Hashreferenz.
*{$stem}{HASH}->{$leaf} spricht durch Dereferenzierung das Hashelement an, dessen Name in $leaf steht. Der Wert dieses Elements ist wiederum ein Typeglob, der mit
*{*{$stem}{HASH}->{$leaf}} angesprochen wird, und somit wird mit
*{*{$stem}{HASH}->{$leaf}}{HASH} dessen Hashslot adressiert, wodurch man wiederum eine Hashreferenz - diesmal auf den gesuchten Stash - erhält. In der Schleife ab Zeile 9 wird dieser Hash dann durch erneutes Dereferenzieren und Auslesen der Elementnamen durchlaufen.
$stem den Wert "main::" und für $leaf den Wert "Alpha::" ein, so ergibt sich für diese Zeilen:
6 my $stem_symtab = *main::{HASH};
7 return unless exists $stem_symtab->{'Alpha::'};
8 my $leaf_symtab = *{$stem_symtab->{'Alpha::'}}{HASH};
woraus die Funktionsweise nun klar erkennbar sein sollte.
In Zeile 10 werden nun nacheinander alle Typeglobs in $leaf_symtab auf undef gesetzt und damit alle Werte mit diesen Namen gelöscht. In Zeile 12 wird der Stash noch einmal explizit geleert, bevor in Zeile 13 das Stashelement aus der übergeordneten Symboltabelle entfernt wird.
Die Funktion ist kurz, aber gründlich - sie löscht das angegebene Package und alle darunter liegenden Packages komplett. Daher ist bei der Anwendung Vorsicht geboten; auf die Probleme, die sich aus schreibendem (bzw. löschendem) Zugriff auf Stashes ergeben können, wurde schon weiter oben hingewiesen.
Zum Abschluss sei noch kurz die Funktion gensym des o.a. Moduls erwähnt - sie liefert eine Referenz auf einen Typeglob zurück, die später z.B. anstelle eines Filehandles verwendet werden kann:
package Symbol;
my $genseq = 0;
my $genpkg = "Symbol::";
sub gensym () {
my $name = "GEN" . $genseq++;
my $ref = \*{$genpkg . $name};
delete $$genpkg{$name};
$ref;
}
Hier wird ein temporärer Name für den Typeglob ( Symbol::GEN<n> ) erzeugt und anschließend auf diesen Typeglob eine Referenz gelegt. Das entsprechende Element wird sodann wieder aus dem Stash gelöscht, der Typeglob bleibt aber durch die auf ihn zeigende Referenz erhalten, wird also zu einem anonymen Typeglob. Die Referenz wird dann an den Aufrufer zurückgegeben.
Beachte, dass hier sowohl das Anlegen des Typeglobs als auch das Löschen des Stashelementes erst zur Laufzeit geschieht, wenn gensym() aufgerufen wird.
*beta = *alpha;alles in
alpha auch als beta angesprochen werden. Das ist noch nicht besonders interessant, aber sobald unterschiedliche Packages in Spiel kommen, wird die Bedeutung von Aliasing erkennbarer:
package Alpha;
sub testfunc {
...
}
*main::testfunc = *testfunc;
Nun kann auch im Defaultpackage (im Hauptprogramm) die Funktion als testfunc() (ohne Packagenamen) angesprochen werden.
Diese Methode hat allerdings einen nicht ganz unwesentlichen Nebeneffekt - nicht nur die Funktion testfunc ist nun in beiden Packages unter diesem Namen sichtbar, sondern auch alle gleichnamigen Variablen und Handles. Wenn also im Package Alpha und im Hauptprogramm auch jeweils ein Skalar $testfunc verwendet wird, kann es mit diesem Ansatz Probleme geben, weil dann aus beiden Packages ein und dieselbe Variable - vermutlich unabsichtlich - angesprochen wird.
Daher existiert auch eine abgeschwächte Version des Aliasing, die man als partielles Aliasing bezeichnet. Die Idee dahinter ist nicht neu - anstatt den gesamten Typeglob zuzuweisen, wird nur der Slot des gewünschten Wertetyps - in der Regel der Codeslot - zugewiesen, also auf das obige Beispiel bezogen:
*main::testfunc = *testfunc{CODE};
oder die häufigere Schreibweise mit dem Referenzoperator \ :
*main::testfunc = \&testfunc;Hier wird nur der Codeslot von
*main::testfunc auf den Wert von *Alpha::testfunc gesetzt. Diese beiden Slots referenzieren also dieselbe Funktion. Die übrigen Slots beider Typeglobs, so sie verwendet werden, zeigen auf unterschiedliche Werte und daher sind z.B. $main::testfunc und $Alpha::testfunc nach wie vor unterschiedliche Variable.
Und damit wird das Funktionsprinzip des bekannten Exporter -Moduls deutlich, dessen import() Methode mit allen Symbolnamen in @EXPORT und, soferne beim Aufruf angegeben, auch in @EXPORT_OK , partielles Aliasing durchführt. Der folgende Code ist aus Exporter/Heavy.pm entnommen:
1 foreach $sym (@imports) {
2 (*{"${callpkg}::$sym"} = \&{"${pkg}::$sym"}, next)
3 unless $sym =~ s/^(\W)//;
4 $type = $1;
5 *{"${callpkg}::$sym"} =
6 $type eq '&' ? \&{"${pkg}::$sym"} :
7 $type eq '$' ? \${"${pkg}::$sym"} :
8 $type eq '@' ? \@{"${pkg}::$sym"} :
9 $type eq '%' ? \%{"${pkg}::$sym"} :
10 $type eq '*' ? *{"${pkg}::$sym"} :
11 do {require Carp; Carp::croak("Can't export symbol: $type$sym")};
12 }
Ja, diese Zeilen implementieren die Kernfunktionalität von Exporter . Das Modul stellt noch einige weitergehende Möglichkeiten (Export-Tags, Exportieren in ein anderes als das aufrufende Modul, Versionsprüfungen, Symbole zum Exportieren sperren, u.a.m) zur Verfügung, aber letztlich basiert das alles auf obigem Code L<[11]|/item__5b11_5d>.
Was passiert hier? Nun, das Array @imports enthält die Liste aller Namen, die exportiert werden sollen (Funktions- oder Variablenamen). Funktionen können mit oder ohne führendem Sigil & angegeben werden. Die Variable $callpkg enthält den Namen des Packages, in das , $pkg das Package, aus dem exportiert werden soll. Durch die foreach Schleife wird @imports Element für Element abgearbeitet.
In den Zeilen 2 und 3 wird festgestellt, ob dem Namen ein Sigil vorangeht. Wie bereits erwähnt, muss dies bei Funktionsnamen nicht der Fall sein (und ist auch meistens nicht). Falls das Sigil fehlt, wird ein partielles Aliasing mit einer Funktionsreferenz durchgeführt. Damit werden solche Fälle wie
use MyModule qw(xx yy zz);in denen Funktionsnamen ohne
& angegeben werden, abgedeckt.
Ansonsten wurde durch den regulären Ausdruck das Sigil ermittelt und mittels Substitution vom Symbolnamen entfernt, so dass nun ab Zeile 4 je nach Sigil ein partielles Aliasing mit der entsprechenden Referenz erfolgen kann.
Es sollte nun schon klar sein, dass man statt obigem Code auch
5 *{$callpkg.'::'.$sym} =
6 $type eq '&' ? *{$pkg.'::'.$sym}{CODE} :
7 $type eq '$' ? *{$pkg.'::'.$sym}{SCALAR} :
8 $type eq '@' ? *{$pkg.'::'.$sym}{ARRAY} :
9 $type eq '%' ? *{$pkg.'::'.$sym}{HASH} :
10 $type eq '*' ? *{$pkg.'::'.$sym} :
11 do {require Carp; Carp::croak("Can't export symbol: $type$sym")};
hätte schreiben können. Hier hält es Perl wie so oft: TIMTOWTDI L<[12]|/item__5b12_5d>.
Doch halt! Warum wurde in Zeile 10 das {GLOB} weggelassen? Der Leser möge versuchen, es herauszufinden, bevor er in L<[13]|/item__5b13_5d> nachsieht.
Wird mittels des Exporters z.B. ein Skalar exportiert, so wird
*{"${callpkg}::$sym"} = \${"${pkg}::$sym"};
also z.B. für die nach main zu exportierende Variable $hugo des Moduls My_Module
*main::hugo = \$My_Module::hugo;ausgeführt. Dabei wird der im Zielpackage neu angelegte Typeglob als importiert markiert. Diese Importierung ist typ-spezifisch, d.h., sie gilt (in diesem Beispiel) nur für den Skalarwert und nicht für die anderen möglichen Wertetypen. Natürlich können auch Arrays, Hashes oder Funktionen importiert werden, aber das muss durch Plazieren der entsprechenden Namen nach
@EXPORT oder @EXPORT_OK explizit vom Modulautor vorgesehen werden.
Stößt der Parser später (im Package main ) auf den Ausdruck
$hugoso findet er im Typeglob den Skalarwert als importiert gekennzeichnet und erlaubt daraufhin das Verwenden des unqualifizierten Namens. Daher kann man Variable und Funktionen, sobald sie durch den Exporter (oder anderweitiges partielles Aliasing) derart markiert wurden, auch bei aktivem
strict 'vars' ohne Packagenamen ansprechen.
Wird ein Programm mit use strict bzw. use strict 'vars' kompiliert, so muss jede globale Variable zuerst importiert werden, bevor sie ohne Packagenamen angesprochen werden kann. Dazu gibt es die Compilerpragmas vars und subs L<[14]|/item__5b14_5d>. Sieht man sich die import() Methode von vars an, so wird der darin enthaltene, auszugsweise wiedergegebene Code
$sym = "${callpack}::$sym" unless $sym =~ /::/;
*$sym = ( $ch eq "\$" ? \$$sym
: $ch eq "\@" ? \@$sym
: $ch eq "\%" ? \%$sym
: $ch eq "\*" ? \*$sym
: $ch eq "\&" ? \&$sym);
hoffentlich nicht mehr ganz so fremd anmuten. $ch enthält das Sigil, $sym den Namen des zu importierenden Symbols, der vor der Typeglob-Zuweisung ggf. noch mit dem Packagenamen des Aufrufers ergänzt wird, um einen voll qualifizierten Namen zu erhalten. Somit wird daher für das Importieren z.B. eines Skalars $hugo in das Package main durch
use vars '$hugo';nur mehr
$sym = "main::hugo";
*{$sym} = \${$sym};
ausgeführt, was sich zu
*main::hugo = \$main::hugo;reduzieren lässt. Und das entspricht vollkommen dem obigen Code des Exporters, nur mit dem Unterschied, dass hier Ziel- und Quellpackage dasselbe ist. Anders gesagt: das Modul
Exporter und die Pragmas vars und subs basieren auf derselben Idee: sie importieren Symbole - nur sind die Quellpackages andere L<[15]|/item__5b15_5d>.
subs ist eine vereinfachte, auf das Importieren von Funktionen (die ohne Sigil angegeben werden) optimierte Version von vars . subs wird benötigt, um Funktionen vorzudeklarieren , um sie im Code vor der eigentlichen Definition verwenden zu können:
func;
...
sub func {print "!"}
Hier weiß der Parser beim Auffinden des Ausdrucks "func" nicht, was gemeint ist - in der Symboltabelle gibt es noch keinen zugehörigen Typeglob. Daher wird er den Ausdruck als nicht gequotetes Stringliteral interpretieren und sich nach der Ausgabe der Meldung
Unquoted string "func" may clash with future reserved wordnicht weiter darum kümmern. Durch das Einsetzen von
use subs 'func';am Programmbeginn hingegen wird durch partielles Aliasing der Ausdruck
func als Funktionsname bekannt gemacht und der Parser wird ihn ab dann als Funktionsaufruf und nicht mehr als Stringliteral interpretieren.
Kompiliert man folgenden Code
use strict 'vars'; use vars '$alpha'; @alpha = (1,2,3);so sollte nun auch die Bedeutung der erhaltenen Fehlermeldung
Variable "@alpha" is not importedverständlicher werden - der Compiler ist in der letzten Zeile auf den Array-Namen
@alpha gestoßen. Durch die Deklaration in der vorhergehenden Zeile wurde zwar bereits der entsprechende Typeglob angelegt, aber als importiert wurde nur der Skalar markiert, nicht das Array. Da Importings, wie erwähnt, typ-spezifisch sind, muss in diesem Fall durch
use vars qw($alpha @alpha);auch das Array vor dem ersten Ansprechen entsprechend deklariert werden. Fehlt hingegen im obigen Beispiel die zweite Zeile (d.h., es wird überhaupt nichts mit
vars deklariert), so existiert auch noch kein Typeglob und der Compiler bricht mit den wohlbekannten Meldungen
Global symbol "$alpha" requires explicit package name Global symbol "@alpha" requires explicit package nameseine Arbeit ab. Beachte, dass folgender Code (im Package
main )
use strict 'vars'; $main::alpha = 2; print $alpha;nicht funktioniert und der Compiler seine Arbeit ebenfalls mit der
not imported Meldung beendet. Durch die Angabe des vollqualifizierten Variablennamens in der zweiten Zeile wurde der Typeglob *alpha zwar angelegt, aber der Skalar wird nicht als importiert markiert . Der Effekt ist also derselbe wie weiter oben, als der Skalar importiert, aber das Array angesprochen wurde. Das Importing funktioniert nur durch Zuweisung einer Referenz an den jeweiligen Typeglob L<[16]|/item__5b16_5d>.
Aus diesem Umstand erklärt sich auch die globale Auswirkung von vars und subs - sie manipulieren Typeglobs, und die sind (einschließlich eventueller Markierungen) nun einmal im gesamten Programm sichtbar und nicht nur innerhalb bestimmter Blöcke. Und daher gibt es auch kein no vars bzw. no subs - es macht keinen Sinn, einmal erfolgte Importings (deren Existenz ohnehin nur während des Kompilierens mit strict 'vars' von Bedeutung ist) wieder rückgängig zu machen.
Handles dürfen übrigens aufgrund fehlender Deklarationsmöglichkeit immer mit unqualifizierten Namen angesprochen werden, daher ist kein Importing notwendig und es gibt auch keine derartigen Markierungen.
caller innerhalb des Originals vor und nach dem Wrappen dasselbe Resultat zurückliefert.
Durch den Einsatz von Typeglobs läßt sich das recht einfach erreichen. Zum Testen dient die folgende Funktion:
sub hello {
print 'Args: ', join(',', @_), "\n",
'Caller: ', join(',', caller), "\n\n";
}
Der Code, der den Wrapper generiert, sieht wie folgt aus:
1 sub create_wrapper {
2 no strict 'refs';
3 no warnings 'redefine';
4 my $name = caller . '::' . shift;
5 my $oldsub = *{$name}{CODE} or die "Cant't find subroutine '$name'!\n";
6 my $newsub = sub {
7 my ($pkg, $file, $line) = caller;
8 print STDERR "WRAPPER: Hi! $name(@_) was called:\n",
9 "WRAPPER: from '$pkg', file '$file', line $line\n\n";
10 goto &$oldsub;
11 };
12 *{$name} = $newsub;
13 return $oldsub;
14 }
Mittlerweile wird dieser vormals so kryptisch anmutende Code nun hoffentlich schon einigermaßen vertraut wirken - das Wesentliche sind dabei die Zeilen 5 und 12.
In Zeile 5 wird durch Auslesen des Codeslots des Typeglobs, dessen Name create_wrapper als Name der zu wrappenden Funktion übergeben wurde, eine Codereferenz, d.h. ein Zeiger auf diese Funktion in $oldsub abgespeichert (man hätte natürlich auch
$oldsub = \&{$name};
schreiben können L<[17]|/item__5b17_5d>). Existiert die angegebene Funktion nicht, so ist der Slot unbelegt und man erhält undef zurück, worauf mit Ausgabe einer entsprechenden Meldung und Abbruch reagiert wird. Ansonsten wird nun eine neue Funktion - der Wrapper erzeugt, der hier ausgibt, von wem er aufgerufen wurde und anschließend mit
goto &$oldsub;zur ursprünglichen Funktion springt L<[18]|/item__5b18_5d>. Die gezeigte Verwendung von
goto bewirkt, dass der Callstack des Wrappers durch den des Aufrufers ersetzt wird; für die angesprungene Funktion sieht alles so aus, als wenn sie direkt aufgerufen worden wäre. Dieses spezielle goto wird auch in Zusammenhang mit AUTOLOAD -Funktionen häufig verwendet.
Nun wird in Zeile 12 durch die Zuweisung an den Typeglob dessen Codeslot, der bislang immer noch auf das Original zeigt, mit einer Referenz auf die Wrapper-Funktion überschrieben. Das Ergebnis ist, dass nun eine neue Funktion - das "gewrappte" Original - unter demselben Namen aufrufbar ist L<[19]|/item__5b19_5d>.
Zwei Dinge sollen hier noch kurz erwähnt werden:
Da auch hier wieder mit symbolischen Referenzen jongliert wird (Zeile 5 und 12), ist ein no strict 'refs' unerläßlich. Weiters ist Perl sehr misstrauisch, was Zuweisungen an Typeglobs zur Laufzeit betrifft, wenn dabei Codeslots überschrieben werden, und weist mit
Subroutine ... redefined
auf solche Praktiken hin, wenn use warnings (oder der -w Schalter) angegeben wurde L<[20]|/item__5b20_5d>. Innerhalb von create_wrapper ist dieses Misstrauen allerdings ungerechtfertigt, daher wird die Ausgabe dieser Meldung mit dem entsprechenden no warnings 'redefine' Pragma unterbunden. In Perl-Versionen vor 5.6 hätte man (weniger spezifisch, aber genauso effektvoll)
local $^W = 0;
geschrieben.
Die Möglichkeit des Wrappens (oder vollständigen Umdefinierens) von Funktionen zur Laufzeit ist nicht auf selbstbenannte Funktionen beschränkt. Reservierte Funktionen wie AUTOLOAD oder DESTROY , aber auch Methoden (die ja im Prinzip auch nur Funktionen mit einer anderen Aufrufkonvention sind), können genauso damit "behandelt" werden. So kann man das Verhalten von Autoload-Code, aber auch von DESTROY beeinflussen, und, wenn Packages als Klassen verwendet werden, durch Erzeugen neuer benannter Funktionen diesen Klassen Methoden zur Laufzeit hinzufügen oder bestehende abändern.
Nicht funktionieren wird das allerdings mit den reservierten Namen von Blöcken wie z.B. BEGIN , weil diese Namen direkt vom Parser beim Kompilieren erkannt und intern anders abgespeichert werden - ein Typeglob repräsentiert ja nur genau einen Namen, es kann aber z.B. mehrere BEGIN -Blöcke geben.
create_wrapper in der Datei cre_wrp.pl :
hello(qw[a b c]);
my $oldfunc = create_wrapper('hello');
hello(qw[d e f]);
{
no warnings 'redefine';
*hello = $oldfunc;
}
hello(qw[g h i]);
und deren Ausgabe:
Args: a,b,c Caller: main,cre_wrp.pl,1 WRAPPER: Hi! main::hello(d e f) was called: WRAPPER: from 'main', file 'cre_wrp.pl', line 3 Args: d,e,f Caller: main,cre_wrp.pl,3 Args: g,h,i Caller: main,cre_wrp.pl,8In der ersten Zeile wird das Original aufgerufen, das ausgibt, mit welchen Argumenten und von wem es aufgerufen wurde. Danach wird der Wrapper erzeugt und man erhält nun beim Aufruf von
hello() vor dessen Ausgabe noch den Output des Wrappercodes. Beachte, dass die von caller gelieferten Werte - von der Zeilennummer abgesehen - in beiden Fällen dieselben sind, es handelt sich also um einen "echten" Wrapper.
Der Code ab der vierten Zeile zeigt, wie man die Wirkung von create_wrapper wieder rückgängig machen kann. In der zweiten Zeile wurde der von dieser Funktion zurückgelieferte Wert - eine Referenz auf den Code des Originals - in einem Skalar zwischengespeichert. Innerhalb des folgenden Blocks wird nun durch Zuweisung dieses Skalars an den Typeglob wieder der alte Zustand hergestellt; beim nächsten Aufruf von hello() wird daher wieder das Original, ohne Wrapper angesprungen.
Auch hier muss vorher wieder die Ausgabe der redefined Meldung mittels no warnings unterdrückt werden, und die sauberste Art, dies zu tun, ist das Unterdrücken und die Zuweisung innerhalb eines eigenen Blocks (oder ebenfalls in einer Funktion).
Eine denkbare Erweiterung des obigen Codes wäre das Zwischenspeichern auch der zweiten Codereferenz auf den Wrapper und dann (z.B. mittels zweier Funktionen wrapper_on und wrapper_off ) durch Zuweisung der jeweiligen Referenz an den Typeglob zwischen der Version mit Wrapper und der ohne Wrapper "hin- und herzuschalten". Die sich aus dieser Technik ergebenden Möglichkeiten sind außerordentlich vielfältig.
IO Slot dann der Handle angesprochen wird), dann sind die durch die Barewords von Handles vorgegebenen Einschränkungen kein Problem mehr L<[22]|/item__5b22_5d>.
Möchte man den Handle einer geöffneten Datei an eine Funktion zur Verarbeitung übergeben, so kann man
open FH,'input.dat';
process(*FH);
sub process {
my $fh = shift;
while (<$fh>) {
...
}
}
schreiben. An process wird einfach der gleichnamige Typeglob übergeben. Dieser wird innerhalb der Funktion in einen Skalar kopiert und danach über diesen der Typeglob und der darin enthaltene IO Slot angesprochen. Eine weniger schöne Alternative wäre:
sub process {
*SUBFH = shift;
while (<SUBFH>) {...
Hier wird der Typeglob in einen zweiten kopiert und in Befehlen, die die Angabe eines Handles durch ein Bareword vorsehen, dieser verwendet. Diese Alternative ist deswegen weniger schön, weil der innerhalb der Funktion angelegte Typeglob ebenfalls global ist (wodurch man sich die Übergabe als Funktionsargument sparen und gleich *FH hätte verwenden können) und damit die Gefahr besteht, inner- und außerhalb der Funktion unabsichtlich denselben Handle bzw. Typeglob zu verwenden. Außerdem ist damit ein rekursives Verwenden der Funktion nicht möglich (sie ist non-reentrant ).
Abzuraten ist auch von folgendem Code:
$name = 'FH'; open $name,'>output.dat'; # Dasselbe wie open FH,'>output.dat'; print $name 'Hallo, Welt!'; # Dasselbe wie print FH 'Hallo, Welt!;Hinter diesem Code steckt nichts weiter als eine symbolische Referenz L<[23]|/item__5b23_5d>; der Skalar, der als Handle-Argument in
open verwendet wird, enthält hier einen String und daher wird dieser als Handlename (d.h., Name des Typeglobs) verwendet. Die Verwendung von symbolischen Referenzen in Verbindung mit IO Handles ist auch deswegen gefährlich, weil sie leicht zu falschen Annahmen führen kann:
sub process {
my $fh = 'FH';
open $fh,'>temp.tmp.';
print $fh 'Hallo, Welt!';
}
Hier wird innerhalb der Funktion eine lexikalische Variable, die als Filehandle verwendet wird, deklariert. Es liegt die Vermutung nahe, dass nach Beendigung der Funktion durch das Verschwinden der lexikalischen Variable infolge des Blockendes auch die Datei geschlossen wird. Diese Vermutung ist jedoch falsch. Da es sich nur um eine symbolische Referenz handelt, besteht zwischen dem lexikalischen Skalar und dem über ihn angesprochenen Handle kein Zusammenhang; der globale Typeglob (hier also *FH ) und der darin enthaltene IO Handle bleiben nach dem Ende der Funktion unverändert bestehen und somit die betreffende Datei auch weiterhin geöffnet. Die intuitiv erwartete "Bereinigung" lexikalischer Komponenten am Blockende findet hier also nicht statt, und daher sollte man solchen Code - auch im Interesse derer, die ihn später warten müssen - tunlichst vermeiden.
Eine Möglichkeit, das automatische Schließen einer Datei bei Beenden einer Funktion zu erreichen, besteht im Lokalisieren des Typeglobs , der als Handle verwendet wird:
sub process {
local *FH;
open FH,'>temp.tmp';
...
} # temp.tmp wird jetzt geschlossen
Durch das Lokalisieren des Typeglobs werden alle Slots temporär abgespeichert und durch neue, unbenutzte Slots ersetzt. War also bereits eine Datei geöffnet, so bleibt sie geöffnet, kann aber innerhalb der Funktion nicht mehr angesprochen werden. Stattdessen wird ein neu angelegter, lokalisierter IO Slot zum Öffnen der temporären Datei verwendet. Beim Erreichen des Funktionsendes muss Perl wieder den abgespeicherten Slot zurückholen. Dabei erkennt es, dass der temporäre Slot noch belegt ist, und dass daher vor dessen Freigabe die betreffende Datei erst geschlossen werden muss.
Für mehr Informationen zum Lokalisieren - speziell von Typeglobs - siehe perlscopetut.
Mit Perl 5.6 wurde Auto-Vivification mit IO Handles eingeführt. Diese Fähigkeit, die man am besten mit "zum Leben erwecken" übersetzen kann, gibt es auch in anderen Fällen, z.B. wird mit
$alle->[4]->{gamma}->[76] = 'Hallo';
das 77. Element eines anonymen Arrays belegt, auf das das Element gamma eines anonymen Hashes zeigt, welches wiederum über das fünfte Element eines weiteren anonymen Arrays angesprochen wird, auf das schließlich die Referenz in $alle zeigt. Mit Auto-Vivification ist nun hier gemeint, dass alle diese Elemente und Referenzen beim Ausführen dieses einen Befehls automatisch angelegt werden, wenn sie noch nicht existieren.
Das Gleiche ist ab 5.6 bei
open $fh,'>temp.tmp';der Fall, wenn der Skalar
$fh zum Zeitpunkt der Ausführung noch auf undef gesetzt ist. Da der Skalar noch nichts (d.h, auch keinen String, der als symbolische Referenz verwendet werden könnte) enthält, wird automatisch ein Typeglob und in dessen IO Slot der Handle "zum Leben erweckt" und in den Skalar eine Referenz darauf geschrieben. Handle und Typeglob sind hier aber, wie auch die Arrays und Hashes im Beispiel weiter oben, anonym , d.h., man kann den IO Handle nur über den Skalar, der nun eine echte Referenz enthält L<[24]|/item__5b24_5d>, ansprechen. Und damit wird auch klar, dass man mit
{
open my $fh,'>temp.tmp';
print $fh 'Hallo, Welt!';
...
}
nun auch erreichen kann, dass eine Datei bei Verlassen des Blocks (oder einer Funktion) automatisch geschlossen wird: da der lexikalische Skalar der einzige Bezugspunkt zum anonymen Typeglob ist, kann dieser, nachdem der Skalar verschwunden ist, ebenfalls entfernt werden und damit wird vorher implizit die Datei, die über sein IO Handle geöffnet war, geschlossen.
Auto-Vivification wird natürlich nicht nur von open , sondern auch von alle anderen Builtins, die Handles anlegen (z.B. opendir , pipe , sysopen , socket und accept ) unterstützt.
Aber auch Benutzer älterer Perl-Versionen brauchen auf diese Funktionalität nicht zu verzichten: es gibt zwar noch keine Auto-Vivification, aber die Verwendung von Handle-Referenzen ist genauso unterstützt. Man muss Perl nur etwas unter die Arme greifen:
my $fh = \*FH; # Erzeuge Globreferenz
delete $main::{FH}; # Loesche Stashelement (Typeglob wird anonym)
open $fh,'>x.x'; # Weiter wie gehabt
Hier wird zunächst eine Referenz auf einen benannten Typeglob angelegt. Anschließend wird der Eintrag des Globs aus der Symboltabelle (Stash) wieder gelöscht L<[25]|/item__5b25_5d>, der Typeglob wird dadurch anonym. Nun kann der Skalar genauso wie beim Auto-Vivifying als Handle verwendet werden; auch hier wird die Datei automatisch geschlossen, wenn der Bereich verlassen wird, innerhalb dessen $fh sichtbar ist.
In diesem Zusammenhang soll auch noch die Funktion
Symbol::geniosym();erwähnt werden, deren Code vereinfacht als
sub geniosym {
my $name = 'GEN'. $genseq++;
my $sym = \*{'Symbol::' . $name};
delete $Symbol::{$name};
select(select $sym);
return *{$sym}{IO};
}
dargestellt werden kann. Die ersten drei Zeilen erzeugen, wie weiter oben beschrieben, einen anonymen Typeglob. Die vorletzte Zeile erzwingt durch die Verwendung von select die Initialisierung des IO Slots des erzeugten Typeglobs (dadurch wird vermieden, dass die Funktion eine Referenz auf undef zurückliefert), und die letzte Zeile gibt das Handlesymbol (eine Referenz auf den IO Slot) zurück. Dadurch kann der Slot direkt über die Referenz und somit schneller als über einen Typeglob angesprochen werden, allerdings ist die Erzeugung, wie man sieht, dafür umständlicher. Der von geniosym zurückgelieferte Wert ist somit keine Glob-, sondern eine IO (Handle) Referenz L<[26]|/item__5b26_5d>.
Tatsächlich kann man an open (und die anderen Funktionen zum Anlegen von Filehandles) folgendes direkt oder in einem Skalar übergeben:
$fh = undef; open $fh,'x.x'; # Auto-Vivifying
$fh = \*GLOB; open $fh,'x.x'; # Globreferenz
$fh = *GLOB; open $fh,'x.x'; # Typeglob
$fh = *GLOB{IO}; open $fh,'x.x'; # IO (Handle) Referenz
wobei Methoden 1 und 4 erst ab Perl 5.6 unterstützt sind L<[27]|/item__5b27_5d>. Das mit Methode 1 durchgeführte Auto-Vivifying entspricht, wie weiter oben gezeigt, der Globreferenz von Methode 2.
Das Arbeiten mit lexikalischen Globreferenzen ist die einfachste und sicherste Möglichkeit, mit Handles umzugehen. Bis Perl 5.6 wurden stattdessen Typeglobs direkt verwendet und waren zumindest in diesem Bereich absolut unentbehrlich. Da man immer wieder auf Code, der typeglob-basierende Handles verwendet, stoßen kann, macht es Sinn, auch heute noch darüber Bescheid zu wissen.
Das ist nur ein Beispiel zum Veranschaulichen.
Hier geht es wieder Zurück. Von dieser Regel gibt es Ausnahmen: die Symbole ARGV , ARGVOUT , ENV , INC , SIG , STDERR , STDIN und STDOUT werden immer im Stash %main:: angelegt (und später dort gesucht), auch wenn mit package gerade ein anderes Package ausgewählt ist. Sollen diese Symbole in anderen Stashes angelegt werden, so ist der Symbolname immer vollqualifiziert anzugeben. Jedoch bleibt die spezielle Bedeutung der einzelnen Variablen auf jene im Stash %main:: beschränkt.
Beachte, dass hier von Symbolen die Rede ist: Obiges trifft daher z.B. auf $ENV , @ENV , %ENV , &ENV , das Dateihandle ENV und das Formathandle ENV zu, d.h., auf alle Wertetypen mit den angeführten Namen ( *ENV ).
Es trifft weiters auch auf alle Interpunktions-Variable (solche, deren Name aus keinem alphabetischen Zeichen besteht oder damit beginnt) zu. Bei solchen Variablen erlaubt der Parser außerdem keine Angabe eines Packagenamens, so dass diese Variable immer nur im Stash %main:: vorkommen können.
"Wissen" ist hier im übertragenen Sinn zu verstehen - die Stashelemente werden bereits beim Kompilieren angelegt und der vom Compiler generierte Code greift nur über deren Adressen auf den Inhalt zu. Der Zugriff über den Stash wird nur notwendig, wenn ein Variablenname erst zur Laufzeit gebildet wird, wie das z.B. in Code wie
my $package = 'Hugo';
my $name = 'Test';
${$package.'::'.$name} = 'Hallo';
geschieht (d.h., über symbolische Referenzen) . Hier kann beim Kompilieren keine Elementadresse verwendet werden, da Stash- und Keynamen noch nicht bekannt sind.
Zurück Aus Implementierungsgründen ist der Skalar-Slot eines Typeglobs immer belegt; wird er nicht verwendet, so enthält er den Wert undef (bzw. eine Referenz darauf).
Und Sache desjenigen, der später solchen Code warten muss.
Wie bereits erwähnt, laufen diese Vorgänge beim Kompilieren ab; zur Laufzeit wird nur mehr über die vom Compiler generierte Adresse auf den Typeglob zugegriffen.
Es ergeben sich außerdem erhebliche Unterschiede im Umgang mit lexikalischen Variablen, die außerhalb einer Funktion deklariert, aber innerhalb der Funktion angesprochen werden. Siehe perlscopetut.
Das gilt übrigens auch für Funktions- und Handle-Bezeichnungen. Anders gesagt, bezieht sich use strict 'vars' - Nomen est omen - nur auf Variablenamen .
Es wird an den Typeglob eine Referenz auf undef zugewiesen. undef ist ein skalarer Wert, daher handelt es sich um eine Skalarreferenz, die den Skalarslot des Typeglobs löscht. Anders gesagt bewirken die folgenden Befehle dasselbe:
*hugo = \undef; $hugo = undef;
Beachte, dass der vom Compiler generierte Code jedoch unterschiedlich ist, d.h., die beiden Befehle bewirken dasselbe, sind aber nicht identisch .
Genauer gesagt, enthält der Hash-Slot des Typeglobs von $main::{main::} eine Referenz auf %main:: . Anders wäre es nicht möglich, wie im Beispiel gezeigt, mit
*main::{HASH}
auf die oberste Symboltabelle zuzugreifen. Da ein Typeglob immer in einem Element eines Stashes abgelegt ist, muss es natürlich auch für das Element main:: einen Hash - eben %main:: - geben. Somit könnte man den Typeglob *main auch als Hashelement
$main::{'main::'}
ansprechen. Puh!
(Beachte, dass hier der Keyname gequoted werden muss, um den Parser nicht durcheinander zu bringen, weil dieser ansonsten die Doppelpunkte "verschlucken" würde. Auch ein Parser hat irgendwann einmal genug!).
Wobei man im Kopf behalten sollte, dass dieser Code im Context eines BEGIN -Blocks abgearbeitet wird, d.h., bevor die durch das partielle Aliasing angelegten Variablen angesprochen werden.
There Is More Than One Way To Do It - es gibt mehrere Wege, (in Perl) etwas zu tun. Eines der Mottos der Perl-Gemeinde, das auf den Umstand anspielt, dass Perl's Parser in seinen Syntaxprüfungen sehr großzügig ist und es daher fast immer etliche Möglichkeiten gibt, ein und dasselbe Problem mit teilweise erheblich unterschiedlichem Code zu lösen.
Der Ausdruck
*{$pkg.'::'.$sym}{GLOB}
liefert eine Referenz auf den Typeglob. Das ist hier aber nicht verlangt, vielmehr soll der Exporter bei einer Angabe von *symnam tatsächlich ein vollständiges Aliasing durchführen, und das geschieht bekanntlich durch Zuweisung eines Typeglobs direkt an einen anderen. Daher fehlt im ursprünglichen Code auch der Referenzoperator vor dem '*' .
In den laufenden Perl-Versionen sind die Zuweisungen
*alpha = \*beta; *alpha = *beta;
identisch; der Compiler generiert zwar genau diesen Code (mit und ohne Referenzoperator), aber im zweiten Fall wird das \ zur Laufzeit verworfen. Dieses Verhalten ist allerdings nicht festgelegt und kann sich in künftigen Perl-Versionen ändern (bzw. war möglicherweise in früheren Versionen schon anders), daher wurde im Exporter darauf Bedacht genommen und man sollte das ggf. auch selber tun.
Auf die seit Perl 5.6 bestehende Möglichkeit, globale Variable mit our zu deklarieren, wird in perlsub und perlscopetut eingegangen.
Es gibt noch einen subtilen Unterschied zwischen Exporter.pm und vars.pm . Letzterer weist auch im Fall eines Typeglobs (z.B. use vars '*hugo' ) eine Referenz an den Zieltypeglob zu. Der Exporter führt hier gleich ein direktes Aliasing durch. Aber wie in L<[13]|/item__5b13_5d> schon angeführt, macht das (zumindest in den aktuellen Perl-Versionen) keinen Unterschied, was nicht heißt, dass sich das nicht künftigen Versionen ändern kann und dann ein Anpassen des vars Pragmas notwendig wird.
Ident hingegen ist beiden Modulen, dass ihr Code als BEGIN -Block, d.h., während der Compilerphase abläuft und durch Aliasung bzw. Importing das Verhalten des Compilers beeinflusst.
Außerdem muss tatsächlich etwas importiert werden, d.h., das gerade eingestellte Package muss unterschiedlich sein zu dem, in das importiert wird. Folgender Code würde daher nicht funktionieren:
use strict 'vars';
package main;
BEGIN {
*main::hugo = \$main::hugo;
}
$hugo = 2;
Wird in diesem Beispiel der Name in der package -Anweisung geändert und vor die Zuweisung an $hugo ein package main; eingefügt, dann wird der Code funktionieren.
Im Falle der Pragmas ist das kein Problem, da ja während der Abarbeitung ihres Codes ohnehin gerade die Packages vars bzw. subs aktiv sind.
Obwohl die beiden Schreibweisen
*glob = *func{CODE};
*glob = \&func;
letztlich dasselbe bewirken, werden unterschiedliche Tätigkeiten durchgeführt (und daher ist auch der kompilierte Code unterschiedlich): im ersten Fall wird der Slot eines Typeglobs ausgelesen und die darin enthaltene Referenz zugewiesen. Im zweiten hingegen wird der Funktionswert als solcher angesprochen und erst eine Referenz darauf erzeugt, bevor die Zuweisung durchgeführt wird. Anders gesagt liefert das zweite Konstrukt zur Laufzeit genau denselben Wert, der im Slot des Typeglobs bereits durch den Parser beim Kompilieren des Codes abgelegt wurde.
Meistens wird die zweite Schreibweise verwendet, obwohl eigentlich die erste transparenter macht, was hier tatsächlich zugewiesen wird. Außerdem ist letztere, da lediglich ein bereits vorhandener Wert kopiert wird, effizienter.
Tatsächlich ist diese Schreibweise unrichtig - genau genommen, bedeutet
goto &$oldsub;
"Rufe die Funktion, deren Name (oder Referenz) in $oldsub steht, auf, und gehe anschließend zu jener Stelle, auf die die von ihr zurückgelieferte Codereferenz zeigt". Da das aber in den seltensten Fällen wirklich gewüscht ist, zeigt der Parser hier Intelligenz, indem er daraus ungefragt ein
goto \&$oldsub;
macht, was tatsächlich dem entspricht, was erwartet wird. Und damit ist klar, dass man kürzer auch gleich
goto $oldsub;
schreiben kann. Kurioserweise findet man in der Fachliteratur fast immer die obige, eigentlich unrichtige Schreibweise vertreten, weswegen sie auch im Beispiel beibehalten wurde. Trotzdem soll auf diese Ungenauigkeit hingewiesen werden.
Tatsächlich erzeugt create_wrapper eine Closure , d.h., eine Funktion, die eine lexikalische Variable ( $oldsub ), die außerhalb ihres Geltungsbereiches liegt, anspricht. Dadurch wird der Wert der Variablen, den diese zum Zeitpunkt der Definition der Closure hatte, darin "eingeschlossen" (Name!). Es können beliebig viele derartige Closures gleichzeitig erzeugt werden, jede mit ihrem eigenen Wert für $oldsub . Mehr dazu in perlref und perlscopetut.
Während Closures meistens nur als anonyme Subroutinen angelegt werden, erhalten sie hier - da sie ja bestehende, benannte Funktionen ersetzen sollen - durch Zuweisung ihrer Codereferenzen an Typeglobs ebenfalls Namen.
Und der wird doch immer angegeben, oder etwa nicht? ;-)
Zu unrecht, wie ich meine. Das in Perl eingebaute, automatische Formatierungssystem, das mit den Variablen $. , $% , $= , $- , $~ , $^ , $: und $^L und den Befehlen format (Deklaration) und write (Ausführung) gesteuert wird, ist sehr leistungsfähig und Perl verdankt das "r" in seinem Namen ( Practical Extraction & Report Language ) nicht zuletzt diesem System. Ein Blick in perlform lohnt sich allemal. Die einzige Einschränkung ist, dass Formate, wie auch Packages, Subroutinen und lexikalische Variable deklariert werden müssen und daher bereits beim Kompilieren namentlich bekanntzugeben sind.
Auf die sich durch das Auto-Vivifying ab Perl 5.6 ergebenden Möglichkeiten wird weiter unten eingegangen; es werden zunächst die Wege, die bereits in älteren Perl-Versionen vorhanden waren, erläutert.
Was man bei der Verwendung von strict 'refs' auch schnell merkt.
Man kann sich davon mit
print ref $fh; # Gibt 'GLOB' (d.h., Globreferenz) aus
überzeugen.
Hier unter der Annahme, dass der Code im Package main ausgeführt wird.
Allerdings wird man über die Ausgabe von
use Symbol 'geniosym'; my $sym = geniosym(); print ref $sym; # Gibt 'IO::Handle' aus
etwas erstaunt sein - ein IO Handle (d.h., die IO Datenstruktur, auf die die Referenz in $sym zeigt), ist aus implementierungstechnischen Gründen stets ein Objekt des Types IO::Handle (d.h., eine Referenz auf einen Wert, der mit dieser speziellen Klasse "gesegnet" wurde). Dieser Mechanismus ermöglicht das Verwenden von IO Handles als Objektinstanzen. So sind folgende Befehle (weitgehend) identisch:
print $sym 'Hallo';
$sym->print('Hallo');
oder, da hier auch wieder Typeglobs als Stellvertreter verwendet werden können:
print FH 'Hallo'; # oder: print {*FH} 'Hallo';
FH->print('Hallo'); # oder: *FH->print('Hallo');
Auch wenn es nicht so aussieht, ist FH hier kein Klassenname, sondern eine Objektinstanz - genauer gesagt wird damit das durch den IO Slot des Typeglobs *FH repräsentierte Objekt und darüber die Methode print angesprochen. Ganz schön gefinkelt, nicht wahr?
Der objekt-orientierte Ansatz hat den Vorteil der größeren Flexibilität: durch Vererbung können Methoden hinzugefügt werden und damit kann der Funktionsumfang eines IO Handles weit über die von Perl's Builtin-Funktionen bereitgestellten Möglichkeiten hinausreichen. Außerdem wird dadurch die Benutzung von Funktionen oder Eigenschaften, die sich auf das gerade aktuelle Handle beziehen, erleichtert - "das gerade aktuelle" Handle ist dann die Objektinstanz, die beim Aufruf der jeweiligen Methode angegeben wird. Das macht Code, der mit solchen Methoden arbeitet, transparenter.
Man kann in Perl-Versionen vor 5.6 auch eine noch nicht verwendete Handlereferenz an open und Konsorten übergeben, man muss sie nur vorher über den Typeglob initialisieren :
select (select *GLOB); # Initialisierung
my $fh = *GLOB{IO}; # IO-Referenz holen
open $fh,'x.x'; # An open() uebergeben
Der select Befehl legt eine Handlestruktur an und läßt den IO Slot des angegebenen Typeglobs darauf zeigen. Danach kann dieser in den Skalar übernommen (oder auch direkt mit open() angegeben) werden.
Nicht, dass man das umbedingt brauchen würde, es sollte nur der Vollständigkeit halber gezeigt werden. :-)
Dieser Umstand (ein IO Slot musste bis Perl 5.6 vor der ersten Benutzung initialisiert werden), ist auch der Grund dafür, dass dies, wie weiter oben gezeigt, in Symbol::geniosym (das natürlich auch noch ältere Perl-Versionen unterstützt) immer noch geschieht. In perldata (Abschnitt Typeglobs und Filehandles ) wird sogar extra darauf hingewiesen:
That's because *HANDLE{IO} only works if HANDLE has already been used
as a handle. In other words, *FH must be used to create new symbol
table entries; *foo{THING} cannot.
Ab Perl 5.6 stimmt das nicht mehr.
bol@adv.magwien.gv.at . Alle Rechte vorbehalten.
Dieses Dokument darf beliebig verteilt und die darin enthaltenen Code-Beispiele können beliebig verwendet und den jeweiligen Gegebenheiten angepasst werden. Dokumentationen, die daraus abgeleitet sind, müssen diesen Copyright-Hinweis vollständig enthalten. Es gelten ansonsten die Lizenzbestimmungen der jeweiligen Perl-Distribution.
-- FerryBolhar - 23 Aug 2007
-- HaraldBongartz - 04 Sep 2007
| I | Attachment | Action | Size | Date | Who | Comment |
|---|---|---|---|---|---|---|
| |
perlglobtut.pod | manage | 78.0 K | 2007-09-06 - 13:40 | FerryBolhar | Neue Perl-Doku: Typeglobs, Stashes und Exporter |