'Type Globs'
und Referenzen
'Type Globs'
Manchmal möchte man nicht einen Array als Wertparameter übergeben, sondern
nur dessen Name, um innerhalb der Subroutine mit der globalen Variablen
arbeiten zu können (PASCAL: VAR-Parameter). 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. Typeglobs sind somit Einträge
in der Symboltabelle. (Diese ist im Hash %:: abgespeichert.)
Beispiel:
$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
Anfangs dieses Kapitels haben wir Typeglobs verwendet, um VAR-Parameter
zu simulieren. Jetzt können wir dies mit Referenzen tun. Das ist
übrigens 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 vorletzten 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.
 
|