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 Manpage zu perldsc bzw. perllol).
1. Referenz erzeugen mit dem Backslash-Operator:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*STDOUT;
2. Referenz auf ein anonymes Array, einen anonymen Hash oder eine anonyme Funktion:
$arrayref = [1, 2, ['a', 'b', 'c']];
$hashref = {'Adam' => 'Eve', 'Clyde' => 'Bonnie');
$coderef = sub { print "Boink!" };
3. Referenzen werden häufig durch spezielle Subroutinen, den Konstruktoren zurückgegeben:
$objref = new Doggie (Tail => 'short', Ears => 'long');
$objref->bark(); # Aufruf einer Methode
1. Überall, 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";
2. Überall, 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
3. 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";
4. 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.
Details dazu finden sich auf den Manpage perldsc und perllol.
Weitere Angaben über Referenzen findet man in den perlref-Manpage.
Die Verwendung von Referenzen ist die einzige Möglichkeit, mehrere Arrays oder Hashes als Parameter an eine Subroutine zu übergeben. (Warum?)
Das folgende Beispiel gibt die letzten Elemente von einer Liste von Arrays aus:
my @vornamen = ('Hans', 'Peter');
my @nachnamen = ('Müller', 'Maier');
my @namen_kombiniert = kombiniere_namen(\@vornamen, \@nachnamen);
sub kombiniere_namen {
my $vornamen_aref = shift;
my $nachnamen_aref = shift;
my @retlist = ();
foreach my $vorname ( @{$vornamen_aref} ) {
foreach my $nachname ( @{$nachnamen_aref} ) {
my $kombination = "$vorname $nachname";
push @retlist, $kombination;
}
}
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);
}
}
Man nehme die Übung 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.