
'Type Globs' und Referenzen
Typeglobs sind Einträge in die Symboltabelle. (Diese ist im Hash %::
abgespeichert.) In Perl kann man alle Objekte mit einem bestimmten Namen referenzieren,
indem man diesen Namen mit dem Präfix * versieht: *foo. Das nennt man
dann type globbing, weil der Stern als Wildcard für alle anderen
Präfix-Buchstaben wie $, %, & stehen kann. Folgendes Beispiel soll diesen
Sachverhalt erhellen:
$foo = 100;
@foo = ('aa','bb','cc');
sub foo {
print "I am foo\n";
}
sub deref {
local(*bar) = @_;
print $bar[0],"\n";
print $bar,"\n";
&bar;
}
print "Ref: ",*foo,"\n";
&deref(*foo);
Die Ausgabe dieses Programmes sieht wie folgt aus:
Ref: *main'foo
aa
100
I am foo
In Perl 4 wurde dieser Mechanismus verwendet, um die Uebergabe von Referenzen an
Subroutinen zu simulieren. In Perl 5 werden dafür echte Referenzen verwendet (siehe
unten). Natürlich funktioniert der alte Mechanismus auch in modernen Versionen.
Referenzen
Mir dem type globbing erhält man symbolische Referenzen, dh. sie beinhalten den
Namen einer Variablen, vergleichbar mit einem symbolischen Link in einem Unix-Filesystem.
In Perl 5 gibt es auch sogenannte harte Referenzen. Sie zeigen direkt auf das der
Variabeln zugrundeliegende Objekt, welches eine skalare Variable, ein Array oder ein
assoziativer Array (Hash) sein kann, aber auch eine Subroutine oder ein Filehandle. Diese
Referenzen sind 'intelligent'. Sie führen Buch über die Anzahl Referenzen auf ein Objekt
und geben das Objekt frei, sobald diese Anzahl auf Null geht.
Eine Variable wird von Perl nie implizit dereferenziert. Falls eine skalare Variable eine
Referenz ist, wird sie sich immer als skalare Variable verhalten und nicht als der Typ,
den sie referenziert. Diese Tatsache hat syntaktische Konsequenzen (siehe perldsc- , resp. perlLoL-
Manpage).
Erzeugen von Referenzen
- Mit dem Backslash-Operator:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*STDOUT;
- Als Referenz zu einem anonymen Array, Hash oder Funktion:
$arrayref = [1, 2, ['a', 'b', 'c']];
$hashref = {'Adam' => 'Eve', 'Clyde' => 'Bonnie');
$coderef = sub { print "Boink!" };
- Referenzen werden häufig durch spezielle Subroutinen, den Konstruktoren zurückgegeben:
$objref = new Doggie (Tail => 'short', Ears => 'long');
$objref->bark(); # Aufruf einer Methode
- Referenzen zu Filehandles werden durch Referenzen zu einem Typeglob erzeugt.
Folgendes Beispiel zeigt, wie ein Filehandle als Parameter einer Subroutine übergeben
wird:
splutter(\*STDOUT); # Aufruf
sub splutter { # Deklaration
my $fh = shift;
print $fh "gugus\n";
}
Dereferenzieren
- Ueberall, wo man einen Identifier als Teil eines Variablen- oder Subroutinennamens
schreiben würde, kann dieser Identifier durch eine einfache skalare Variable, welche eine
Referenz auf den gewünschten Typ darstellt, ersetzt werden.
$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = "Januar";
$$hashref{"KEY"} = "VALUE";
&$coderef(1,2,3);
print $globref "output\n";
- Ueberall, wo man einen Identifier als Teil eines Variablen- oder Subroutinennamens
schreiben würde, kann dieser Identifier durch einen BLOCK, welcher eine Referenz auf den
gewünschten Typ zurückgibt, ersetzt werden.
$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "Januar";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
In diesen Fällen ist es natürlich überflüssig die geschweiften Klammern zu
verwenden. Aber da ein Block einen beliebigen Ausdruck beinhalten kann, gibt es
vernünftigere Beispiele:
&{ $dispatch{$index} }(1,2,3); # Aufruf der korrekten Subroutine
- Als syntaktische Variante geht auch:
$arrayref->[0] = "Januar";
$hashref->{"KEY"} = "VALUE";
Die linke Seite vom Pfeil kann irgendein Ausdruck sein, welcher eine Referenz
zurückgibt.
$array[$x]->{"foo"}->[0] = "Januar";
Vor dieser Anweisung könnte $array[$x] undefiniert sein, wird aber an dieser
Stelle automatisch zu einer Referenz auf einen Hash. Dasselbe gilt analog für $array[$x]->{"foo"}.
Die Pfeile zwischen den Klammern müssen nicht geschrieben werden:
$array[$x]{"foo"}[0] = "Januar";
- Eine Referenz kann aber auch eine Referenz auf ein Objekt (zu einer Klasse) sein. Dann
gibt es vermutlich Methoden, welche durch diese Referenz zugegriffen werden können:
$obj->methode($param);
Wir werden im nächsten Kapitel mehr davon hören. Und dann gibt es noch die perlobj-Manpage.
Mit den Referenzen von Perl 5 ist es einfach mehrdimensionale Datenstrukturen (Arrays von
Arrays, Arrays von Hashes, Hashes von Subroutinen etc. zu erzeugen (siehe perldsc- resp. perlLoL-Manpage).
Weitere Angaben über Referenzen findet man in den perlref -Manpage.
Referenzen als Parameter von Subroutinen
Manchmal möchte man nicht einen Array als Wertparameter übergeben, sondern innerhalb
der Subroutine mit der globalen Variablen arbeiten (PASCAL: VAR-Parameter). Natürlich ist
es schneller eine Referenz zu übergeben, anstelle eines ganzen Arrays, daneben ist es die
einzige Möglichkeit, mehrere Arrays als Parameter zu übergeben. (Warum?)
Das folgende Beispiel gibt die letzten Elemente von einer Liste von Arrays aus:
@letzte = popmany( \@a, \@b, \@c, \@d );
sub popmany {
my $aref;
my @retlist = ();
foreach $aref (@_) {
push @retlist, pop @$aref;
}
return @retlist;
}
Das ist ja alles sehr schön, aber wie bekomme ich mehrere Arrays oder Hashes als
Rückgabewerte? Wie wär's mit folgendem:
($aref, $bref) = func(\@a, \@b);
print "@$aref has more then @$bref\n";
sub func {
my ($cref, $dref) = @_;
if (@$cref > @$dref) {
return ($cref, $dref);
} else {
return ($dref, $cref);
}
}
Uebung
Man nehme die Uebung des letzten Kapitels und tausche die Parameter der Subroutine aus.
Damit das funktioniert, muss man eine Referenz auf den Array der Textstücke übergeben
und in der Subroutine den Array dereferenzieren.
|