Perl: Referenzen auf Variablen und Funktionen

In Perls Objektorientierter Programmierung spielen Referenzen eine wesentliche Rolle

In Perl kann eine Referenz nur zusammen mit dem abstrakten Datentyp erzeugt werden, z.B. wenn die Variable oder Subfunktion definiert wird (Stand Perl v5.16) oder bereits als Variable oder Subfunktion vorliegt, Beispiele:

# Blanko Referenzen die später mit Werten
# befüllt werden können
my $href = {}; # Hashreferenz
my $aref = []; # Arrayreferenz

# Die Funktion liefert ein Array
# untenstehend wird gleich eine Referenz
# auf das Array erzeugt
my $loc  = [localtime];

Als abstrakte Datentypen werden Array's und Hashes verstanden.

Referenzen auf Scalare, Arrays und Hashes

Ein der Variablen vorangestellter Backslash erzeugt die Referenz:

# Referenz auf den Hash %ENV
my $env = \%ENV;

# Erzeuge eine Referenz auf ein Array
my @arr = qw(Anton Berta Cäsar);
my $aref = \@arr;

# Referenz auf ein Scalar
my $name = "Dankwart";
my $nref = \$name;

Dereferenzieren erfolgt entsprechend Scalar, Hash oder Array mit $, % oder @:

print "@$aref"; # Anton Berta Cäsar
print $$nref;   # Dankwart
print join "\n", keys %$env;

Das war die Pflicht, jetzt kommt die Kür:

# Erzeuge eine Hashreferenz
my $href = {
    name => "Dankwart",
    plz  => 55099,
    env  => {
        temp => "20°C",
        hygr => "99%",
    },
};

# zugriff!
print $href->{name};
print $href->{env}->{temp};

# wobei der erste Pfeil genügt
print $href->{env}{temp};

# Achtung Klammer setzen!
print join "\n", keys %{$href->{env}};


# localtime liefert ein Array
# in [] aufgerufen erhalten wir
# gleich die Referenz auf das Array
my $loc = [localtime];

# ein einzelnes Element ausgeben
# verwende den Pfeil-Operator
print $loc->[8]; # is dst 1 oder 0

Der Vorteil in der Verwendung von Referenzen auf Variablen besteht darin, dass es die Originaldaten nur einmal gibt im Hauptspeicher, d.h. damit werden Inkonsistenzen vermieden. Einen besonders hohen Stellenwert haben Hashreferenzen, weil damit viele einzelne Variablen kompakt zusammengefasst werden können und auf diese außerdem namentlich direkt zugegriffen werden kann.

Referenzen auf Funktionen

Code-Referenzen werden mit dem Schlüsselwort sub erzeugt:

# Erzeuge eine Referenz auf eine Funktion
my $coderef = sub{
    print "Hallo @_!";
}; # Semikolon nicht vergessen!

# Rufe die Funktion über die Referenz auf
$coderef->(1,2,3);

# Ausgabe: Hallo 1 2 3!

Das Beispiel zeigt auch, wie in diesem Fall die Argumente übergeben werden.

Gesegnete Referenzen sind Klasseninstanzen

Eine Instanz einer Klasse ist nichts weiter als eine Referenz, die weiß zu welcher Klasse sie gehört -- so schreibt es Eric Foster-Johnson. Betrachte den Code untenstehend:

my $m = bless{}, 'main';

So ist $m eine Hashreferenz, die mit dem Namen der Klasse main gesegnet ist. Gleichermaßen ist $m eine Instanz der Klasse main und mit dieser Instanz lassen sich Funktionen sowie Codereferenzen innerhalb der gleichen Klasse aufrufen:

# Erzeuge eine Referenz auf eine Funktion
my $coderef = sub{
    my $self = shift;
    print "Hallo @_!";
}; # Semikolon nicht vergessen!

# Instanz der Klasse main erzeugen
my $m = bless{}, 'main';

# Rufe die Coderef über die Instanz der Klasse main auf
# Der Pfeil-Operator übergibt die aufrufende Instanz
$m->$coderef(1,2,3);  # Hallo 1 2 3!

# Dasselbe nur anders notiert
$coderef->($m,1,2,3); # Hallo 1 2 3!

Packages und Namensräume

package AnyClass;

# eine ganz normale Deklaration
sub foo{
    my $self = shift;
    my $class = ref $self;
    print "Hallo @_ in Klasse $class!";
}

# Instanz der aktuellen Package
my $m = bless{}, __PACKAGE__;

# Prüfe ob es die Funktion/Methode gibt
my $coderef = $m->can('foo') or die "Kein Code für Funktion 'foo'";

# Methode ausführen
$m->$coderef(1,2,3); # Hallo 1 2 3 in Klasse AnyClass!

Die Package package main; gibt es praktisch immer, auch wenn sie nicht explizit so deklariert ist. Der Code obenstehend deklariert die Klasse main kurzerhand um nach AnyClass. Unabhängig davon, beinhaltet __PACKAGE__ stets den voll qualifizierten Namen derjenigen Package in welcher die Notation erfolgte. Die Funktion can() ist eine Methode, die von der Klasse UNIVERSAL geerbt wird, von dieser Klasse erbt jede in Perl erstellte Package (Klasse) und natürlich auch die Package main oder AnyClass. Soweit die gesuchte Funktion/Methode im aktuellen Namensraum existiert, liefert can() eine Referenz auf diese. Schließlich ermittelt die Funktion ref() den Namen derjenigen Klasse mit welcher die Instanz gesegnet wurde.

Finessen mit Referenzen, Typeglobs

Ab hier kommen ein paar Anregungen aus meiner langjährigen Praxis. Zunächst eine Betrachtung zu den sogenannten Typeglobs, ein Stückchen Code sagt mehr als 1,000 Worte:

# Erzeuge eine Typeglob
*foo = sub{ print "Hallo @_!" };

# type means sub
# und so funktioniert der Aufruf
foo(1,2,3); # Hallo 1 2 3!

Als Nächstes einen Typeglob auf ein Handle, siehe Code untenstehend:

# Argument soll ein Handle sein
sub read_handle{
    my $handle = shift;
    while( read( $handle, my $buffer, 1024) ){
        print $buffer;
    }
};

# Anders kann das Handle
# gar nicht übergeben werden
read_handle(DATA);  # erzeugt Fehlermeldung!
read_handle(*DATA); # so funktioniert es
__DATA__
asdf....
blah-Texte usw.

Über den Umweg eines Typeglob sind auch Zuweisungen wie my $handle = *STDIN; möglich, wird der * (Glob-Operator) nicht notiert, erscheint eine Fehlermeldung, weil es dem Perl-Interpreter in diesem Kontext nicht möglich ist, ein Handle als Type zu erkennen. Wahrend Typeglobs auch mit Array- und Hashreferenzen möglich sind, werden Globs auf Funktionen häufiger verwendet, z.B. um Funktionen mit mehreren verschiedenen Namen zu klonen oder Referenzen auf Funktionen zu erzeugen:

sub foo{ print "This is foo"  }
*bar = *foo;
bar(); # This is foo

# Referenz auf sub über TypeGlob erzeugen
my $ref = *foo;

# Funktion über Code-Ref aufrufen
$ref->(); # This is foo

Referenzen sind auch nur Scalare

Perl versteht als Scalar nicht nur das, was mit einem $ beginnt, sondern auch Referenzen. Wobei es unerheblich ist, ob Referenzen auf Arrays oder Hashes verweisen oder wiederum auf Scalare. Als Schlüssel in einem Hash wird ein String erwartet, daher können Stringbegrenzer auch entfallen die den CODE ohnehin nicht leslicher machen. Der Wert zu einem Hash-Schlüssel jedoch ist grundsätzlich ein Scalar, das kann ein String sein oder aber auch eine Referenz. Allein dieser Fakt ermöglicht tiefergehende Datenstrukturen, die ohne Referenzen also gar nicht möglich wären!

Ohne Referenzen keine OOP

Wie bereits weiter oben festgestellt: Klasseninstanzen sind Referenzen die mit dem Namen der Klasse gesegnet sind. Dieses Segnen (bless) ist nur Referenzen vorbehalten, wobei die Instanz einer Klasse nicht unbedingt eine Referenz auf einen Hash sein muss, ja, es darf auch eine Referenz auf ein Array oder auf ein Scalar sein oder auf ein FileHandle! Für welche Art von Referenz man sich entscheidet, hängt ganz von der konkreten Aufgabenstellung ab.

In der Mehrzahl aller Anwendungsfälle ist eine Klasseninstanz eine Referenz auf einen Hash, wobei Schlüssel in diesem Hash selbstverständlich auch Arrays referenzieren und Array-Elemente wiederum Hashreferenzen sind:

my $self = bless{
    numbers => [1,2,3,4],
    slice   => [{},{},{}],
}, $class;

Literatur

Eric Foster Johnson: "Perl Module" Verlag mitp, ISBN 3-8266-0570-5


Anbieter: nmq​rstx-18­@yahoo.de, die Seite verwendet funktionsbedingt einen Session-Cookie und ist Bestandteil meines nach modernen Aspekten in Perl entwickelten Frameworks.