# Perl - Referenzen

### Harte Referenzen

Sogenannte "harte" Referenzen sind skalare Größen, die auf eine beliebige Variable "zeigen". Das Ziel kann dabei eine einfache skalare Variable sein, aber auch ein Array oder Hash oder auch ein Unterprogramm. Auch kann man eine Referenz auf eine Referenz erstellen.

Beispiele:

    #!/usr/local/bin/perl -w

    $nr = 42;
    $ref_nr = \$nr;

    @planeten = ( 'Erde', 'Mars', 'Jupiter' );
    $ref_planeten = \@planeten;

    %tiere = ( 'Tiger' => 'Indien', 'Löwe' => 'Afrika' );
    $ref_hash = \%tiere;

Wie man sieht, wird eine Referenz einfach dadurch erzeugt, daß vor die Variable ein Backslash ("`\`") gesetzt wird. Unabhängig davon, von welchem Typ das referenzierte Objekt ist, ist die Referenz immer ein Skalar - sie besitzt also die Form `$`name.

Um von einer Referenz wieder zurück zur referenzierten Variablen zu kommen ("Dereferenzierung"), stellt man der Referenz das Symbol voran, das dem Typ des referenzierten Objekts entspricht.

Beispiele:

    #!/usr/local/bin/perl -w

    $nr = 42;
    $ref_nr = \$nr;
    $deref_nr = $$ref_nr;

    @planeten = ( 'Erde', 'Mars', 'Jupiter' );
    $ref_planeten = \@planeten;
    @deref_planeten = @$ref_planeten;

    %tiere = ( 'Tiger' => 'Indien', 'Löwe' => 'Afrika' );
    $ref_tiere = \%tiere;
    %deref_tiere = %$ref_tiere;

Hier muß man also den jeweiligen Datentyp beachten !

(De-)Referenzierung kann auch beliebig mehrstufig angewandt werden:

    #!/usr/local/bin/perl -w

    $stern = 'Sirius';

    $ref_ref_ref_stern = \\\$stern;

    $stern = $$$$ref_ref_ref_stern;

Auch Elemente eines Arrays lassen sich (de-)referenzieren:

    #!/usr/local/bin/perl -w

    @zahlen = ( 'null', 'eins', 'zwei', 'drei' );

    $ref_zahlen = \@zahlen;
    print $$ref_zahlen[2]."\n";

    $ref_zwei = \$zahlen[2];
    print $$ref_zwei."\n";

Im ersten Teil wird hier eine Referenz auf das Array als Ganzes erstellt. Sie wird dann dereferenziert (`$$ref_zahlen`) und von dem Ergebnis (dem Array) das Element mit dem Index 2 ausgewählt. Im zweiten Teil wird dagegen eine Referenz nur eines einzelnen Elements des Arrays erzeugt und wieder dereferenziert.

### "Call by Reference"

Bei dem Aufruf von Unterprogrammen unterscheidet man im wesentlichen zwei Typen, was die Behandlung der Argumente betrifft: "call by value" und "call by reference".

Ersteres beschreibt den Mechanismus, den Perl für gewöhnliche Variablen benutzt, bei dem die Werte (values) der Argumente an das Unterprogramm übergeben werden. Dies bedeutet, daß die lokalen Variablen, denen die Argumente zugewiesen werden, völlig unabhängig von den Variablen existieren, die die Argumente beim Aufruf bestimmen.

Manchmal möchte man aber eine Variable in einem Unterprogramm manipulieren, d.h., etwaige Operationen, die im Rumpf der Subroutine durchgeführt werden, sollen sich auch auf die Variablen im aufrufenden Programmteil auswirken. Eine Möglichkeit besteht darin, die manipulierten Variablen als Rückgabewerte an den aufrufenden Programmteil zu übergeben.

Eleganter und effektiver geht es aber mit Referenzen ("call by reference"). Übergibt man eine Referenz als Argument, so zeigt die (lokale) Referenz auf die Variable im Argument und kann sie direkt bearbeiten.

    #!/usr/local/bin/perl -w

    sub gross_1 {        ### einfacher "call by value"
        my $s = $_[0];
        $s = ucfirst($s);
    }

    sub gross_2 {        ### "call by value" mit Rückgabewert
        my $s = $_[0];
        $s = ucfirst($s);
        return($s);
    }

    sub gross_3 {        ### Übergabe einer Referenz
        my $ref_s = $_[0];
        $$ref_s = ucfirst($$ref_s);
    }

    sub gross_4 {        ### Sonderfall @_
        $_[0] = ucfirst($_[0]);
    }

    print "1) ".($wort = 'kamel')." -> ";
    gross_1($wort);
    print "$wort\n";

    print "2) ".($wort = 'kamel')." -> ";
    $wort = gross_2($wort);
    print "$wort\n";

    print "3) ".($wort = 'kamel')." -> ";
    gross_3(\$wort);
    print "$wort\n";

    print "4) ".($wort = 'kamel')." -> ";
    gross_4($wort);
    print "$wort\n";

    1) kamel -> kamel
    2) kamel -> Kamel
    3) kamel -> Kamel
    4) kamel -> Kamel

Wie man sieht, wirkt sich im ersten Aufruf die Manipulation in "`gross_1`" nicht auf die globale Variable "`$wort`" aus. "`gross_2`" zeigt, wie sich das Problem über den Rückgabewert lösen läßt. In "`gross_3`" wird eine Referenz übergeben, mit Hilfe derer die globale Variable verändert werden kann.

Das vierte Beispiel zeigt, daß das spezielle Array `@_`, das die Unterprogrammparameter enthält, nicht wirklich aus Kopien der übergebenen Variablen besteht. Verändert man die Elemente von `@_`, so wirkt sich dies auch auf die Variablen der Parameter im aufrufenden Programmteil aus. Dieser Trick sollte aber aus Gründen der Übersichtlichkeit nicht unbedingt genutzt werden - er stammt aus der Zeit, als es in Perl noch keine Referenzen gab.

### Referenzen auf Unterprogramme

Auch Referenzen auf Subroutinen sind möglich:

    #!/usr/local/bin/perl -w

    sub hallo { return "Hallo, $_[0] !" };

    $ref_sub = \&hallo;

    print &$ref_sub('Welt')."\n";

    Hallo, Welt !

Bei der Bildung der Referenz ist darauf zu achten, daß sie durch Voranstellen eines Backslashes vor den Namen des Unterprogramms mit dem "`&`" erfolgt. Es dürfen dabei keine Klammern oder gar Argumente angehängt werden (in diesem Falle würde eine Referenz auf den Rückgabewert der Routine gebildet werden). Auch darf das "`&`" nicht weggelassen werden. Analog zur Dereferenzierung bei Variablen muß der Typ des Objektes durch Angabe des entsprechenden Symbols (hier: "`&`") vermerkt werden.

Will man ein Unterpogramm nur über eine Referenz aufrufen, bietet sich eine sogenannte anonyme Subroutine an. Sie besitzt keinen eigenen Namen und liefert bei der Definition eine Referenz auf den Programmcode zurück.

Beispiel:

    #!/usr/local/bin/perl -w

    $ref_sub = sub { return "Hallo, $_[0] !" };

    print &$ref_sub('Welt')."\n";

Man beachte hierbei zwei Dinge: zum einen hat das Unterprogramm, wie schon erwähnt, keinen Namen. Daher folgt unmittelbar auf das Schlüsselwort "`sub`" die Definition in geschweiften Klammern. Außerdem darf nicht übersehen werden, daß es sich hierbei um eine Zuweisung handelt, die durch ein Semikolon abgeschlossen werden muß (es sei denn, diese Zeile steht beispielsweise am Ende eines Blockes).

Referenzen können genauso wie andere skalare Größen als Rückgabewerte von Subroutinen auftreten; d.h., ein Unterprogramm kann eine Referenz auf ein anderes Unterprogramm liefern.

Beispiel:

    #!/usr/local/bin/perl -w

    sub master {
        my $ref_sub;
        $ref_sub = sub { print "Hallo !\n" };
        return($ref_sub);
    }

    $ref_hallo = master();

    &$ref_hallo;

    Hallo !

Ein besonderer Fall in diesem Zusammenhang sind sogenannte "Closures". Dabei betrachtet man Unterprogramme mit lokalen Variablen (deklariert durch `my`). Die genannten lokalen Variablen werden bei der Definition und Zuweisung zu einer Referenz sozusagen mit eingeschlossen.

Beispiel:

    #!/usr/local/bin/perl -w

    sub hallo {
        my $planet = $_[0];
        my $ref = sub { print "Hallo, $planet !\n" };
        return($ref);
    }

    $ref_erde = hallo('Erde');
    $ref_mars = hallo('Mars');

    &$ref_erde;
    &$ref_mars;

    Hallo, Erde !
    Hallo, Mars !

Das Bemerkenswerte daran ist, daß zu dem Zeitpunkt, an dem die anonyme Subroutine ausgeführt wird (hier: am Ende des Skripts), eigentlich der Gültigkeitsbereich der lokalen Variablen `$planet` schon längst verlassen worden ist. Dennoch "erinnern" sich noch beide Referenzen an den lokalen Wert, der zum Zeitpunkt ihrer Zuweisung gültig war.

Autor: Eike Grote | Letzte Änderung: 03.11.2004