This is PerlMonks "Mobile"

Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Referenzen

A translation of this can be found here.

(ASCII Grafik in Anlehnung an PEGS (PErl Graphical Structures) von Joseph Hall)

Was ist eine Referenz?

Es gibt zwei Arten von Referenzen: 'harte' und 'weiche'. Die 'weichen' werden auch 'symbolische' genannt und werden hier nicht weiter behandelt.

Eine Referenz ist eine Art Zeiger auf einen Wert:

+-----------+ +-----------+ | O-----+----->| 12345 | +-----------+ +-----------+

Referenzen werden oft in Variablen gespeichert:
+----------\ +----------\ | a > | b > +----------/ +----------/ | | +-----------+ +-----------+ | O-----+----->| 12345 | +-----------+ +-----------+

Die Variable $a enthält eine Referenz, die auf den Wert der Variablen $b zeigt.


Referenzen können nicht nur auf Werte verweisen, sondern auch auf Arrays und Hashes. Hier ein paar Beispiele:
+----------\ | b > +----------\ +----------/ | a > | +----------/ +-----------+ | | O-----+----->+###########+ +-----------+ +"""""""""""+ | Foo | +----------\ +-----------+ | c > | Bar | +----------/ +-----------+ | | Baz | +-----------+ +-----------+ | O-----+----->| Qux | +-----------+ +-----------+

Referenz $b verweist auf Array @a, Referenz $c verweist auf das vierte Element von @a, $a[3].

Auf diese Weise lassen sich beliebig komplexe Datenstrukturen aufbauen:
+----------\ | a > +-->+###########+ +----------/ | +"""""""""""+ | | | Foo | +-----------+ | +-----------+ | O-----+----->+###########+ | | Bar | +-----------+ +"""""""""""+ | +-----------+ | O-----+---+ +-----------+ | O-----+------>+###########+ +-----------+ +"""""""""""+ | O-----+---+ | Baz | +-----------+ | +-----------+ | | Qux | | +-----------+ | | +-->+###########+ +"""""""""""+ | Baz | +-----------+
Eine Referenz auf eine Liste, die Referenzen auf Listen enthält.


+----------\ | a > +-->+###########+ +----------/ | +"""""""""""+ | | | Foo | +#########################+ | +-----------+ +"""""""""""""""""""""""""+ | | Bar | +-----------\ +-----------+ | +-----------+ | aaa >| O-----+---+ +-----------< +-----------+ | bbb >| O-----+------>+###########+ +-----------< +-----------+ +"""""""""""+ | ccc >| O-----+---+ | Baz | +-----------/ +-----------+ | +-----------+ | | Qux | | +-----------+ | | +-->+###########+ +"""""""""""+ | Baz | +-----------+
Ein Hash, das Referenzen auf Listen enthält.


Wie erzeugt man Referenzen?

Der Backslash Operator \ erzeugt eine Referenz auf einen bestimmten Wert:

$cref = \12345 Referenz auf eine Konstante
$sref = \$s Referenz auf den Wert von $s
$aref = \@a Referenz auf das Array @a
$href = \%h Referenz auf das Hash %h
$fref = \&f Referenz auf die Subroutine &f

Eine Referenz auf ein anonymes Array wird wie folgt erzeugt:
$aref = [123, 456, 789];
Eine Referenz auf ein anonymes Hash:
$href = {NAME => 'Bernd', EMAIL => 'bdulfer@sybase.com'};
Eine Referenz auf eine Anonyme Subroutine:
$fref = sub { print "AnonSub\n" };

Wofür braucht man Referenzen?

Fall 1, Parameterübergabe an Subroutines:
Wenn mehrere Arrays an eine Subroutine übergeben werden, erzeugt Perl aus den Inhalten eine Liste, erreichbar über @_. In der Subroutine besteht keine Möglichkeit festzustellen welche Listenelemente aus welchem Array stammen. Wenn diese Informationen benötigt werden, kann man Referenzen auf die Arrays übergeben. Die Referenzen können dann über @_ zu Arrays aufgelöst werden, die Werte können wieder eindeutig zugeordnet werden.
Außerdem werden wesentlich weniger Daten an die Subroutine übergeben, was die Ausführung des Programmes beschleunigt.

(sinnloses :-) Beispiel:

use Data::Dumper; sub make_hash_from_arrays { my ($aref1, $aref2) = @_; my %hash; $hash{$_} = shift @$aref2 foreach (@$aref1); print Dumper(\%hash); } my @a = ('a', 'b', 'c', 'd'); my @b = (1, 2, 3, 4); make_hash_from_arrays(\@a, \@b);
Die Bedeutung von @$ wird weiter unten erklärt.

Fall 2, komplexe Datenstrukturen:
Ein Adressbuch soll in einer Datenstruktur abgebildet werden.
Ohne Referenzen gäbe es folgende Möglichkeiten:

1. Eine Adresse wird mit Trennzeichen in ein Hashelement geschrieben. Um auf einzelne Daten zuzugreifen, wird split verwendet:

$adresse{Bernd} = 'Bernd Dulfer|Kapellenstr. 1|bdulfer@sybase.com' +; $email = (split(/\|/, $adresse{bernd}))[2];
2. Die Adresse wird auf mehrere Hashes verteilt. Um eine Adresse zu drucken, werden einzelne Elemente aus verschiedenen Hashes benötigt:
$name{Bernd} = 'Bernd Dulfer'; $str{Bernd} = 'Kapellenstr. 1'; $email{Bernd} = 'bdulfer@sybase.com'; print_adresse($name{Bernd}, $str{Bernd});

Beides ist unelegant und unhandlich, die Lösung mit Referenzen:
Ein Hash, das Referenzen auf die Adressen enthält; jede Adresse ist wiederum in einem Hash gespeichert.
+----------\ | adresse > +----------/ | +#######################+ +"""""""""""""""""""""""+ +-----------\ +---------+ | Bernd >| O----+----->+##################################+ +-----------/ +---------+ +""""""""""""""""""""""""""""""""""+ +-----------\ +--------------------+ | Name >| Bernd Dulfer | +-----------< +--------------------+ | Str >| Kapellenstr. 1 | +-----------< +--------------------+ | EMail >| bdulfer@sybase.com| +-----------/ +--------------------+ $adresse{Bernd} = { Name => 'Bernd Dulfer', Str => 'Kapellenstr. 1', EMail => 'bdulfer@sybase.com' }; $email = $adresse{Bernd}->{EMail}; print_adresse($adresse{Bernd});
Wenn zusätzlich zur Adresse auch noch eine Liste mit z.B. ausgeliehenen Büchern gespeichert werden soll, ist dies ohne Referenzen kaum noch zu handhaben. Mit Referenzen sieht es folgendermaßen aus:
+----------\ | adresse > +----------/ | +##########+ +""""""""""+ +----\ +---+ |Bernd>| O-+->+###########################+ +----/ +---+ +"""""""""""""""""""""""""""+ +------\ +------------------+ |Name >|Bernd Dulfer | +------< +------------------+ |Str >|Kapellenstr. 1 | +------< +------------------+ |EMail >|bdulfer@sybase.com| +------< +------------------+ |Buecher>| O---------+->+#####################+ +------/ +------------------+ +"""""""""""""""""""""+ |Der Herr der Ringe | +---------------------+ |Per Anhalter durch...| +---------------------+ |Die Farben der Magie | +---------------------+ $adresse{Bernd} = { Name => 'Bernd Dulfer', Str => 'Kapellenstr. 1', EMail => 'bdulfer@sybase.com', Buecher => [ 'Der Herr der Ringe', 'Per Anhalter durch die Galaxie', 'Die Farben der Magie' ] };

Wie nutze ich Referenzen und Datenstrukturen?

Referenzen werden aufgelöst durch Verwendung des Sigils ($%@&) des Wertes und { }.
Ist die Referenz in einer skalaren Variablen gespeichert, können die geschweiften Klammern weggelassen werden.
Beispiele:

Eine Referenz auf einen skalaren Wert:
$s = 'qwertz'; $sref = \$s; print ${$sref}, "\n"; print $$sref, "\n";
Gibt :
qwertz qwertz
aus.

Eine Referenz auf ein Array:
$, = ':'; @a = (123, 456, 789); $aref = \@a; print @{$aref}, "\n"; print @$aref, "\n";
Gibt:
123:456:789 123:456:789
aus.

Auf einzelne Elemente wird mit
${$aref}[1] oder $$aref[1]
zugegriffen.

Sind die Referenzen in einem Array oder Hash gespeichert, kommt der 'Dereferenzierer' -> ins Spiel:
$adresse{Bernd} = { Name => 'Bernd Dulfer', Str => 'Kapellenstr. 1', EMail => 'bdulfer@sybase.com', Buecher => [ 'Der Herr der Ringe', 'Per Anhalter durch die Galaxie', 'Die Farben der Magie' ] }; print 'Name: ', $adresse{Bernd}->{Name}, "\n";
Gibt
Name: Bernd Dulfer
aus.

Auf dieselbe Weise werden Zuweisungen vorgenommen:
$adresse{Bernd}->{Buecher}->[3] = 'Goedel, Escher, Bach - Ein endl +os geflochtenes Band';
Die Referenz in $adresse{Bernd} wird aufgelöst und liefert ein Hash. Aus diesem Hash wird das Element mit dem Schlüssel 'Bernd' genutzt, dessen Wert eine Referenz ist. Diese wiederum wird auch aufgelöst und liefert eine Liste, deren viertes Element einen neuen Wert zugewiesen bekommt.


Soll etwas dereferenziertes nicht als Skalar sondern z.B. als Array genutzt werden, muß es mit Sigil und { } dereferenziert werden:

push @{$adresse{Bernd}->{Buecher}}, 'Die Eissegler von Tran-ky-ky' +;
$adresse{Bernd} enthält eine Referenz auf ein anonymes Hash, auf das man mittels -> zugreifen kann.
$adresse{Bernd}->{Buecher} enthält eine Referenz auf ein anonymes Array.
Um das Array zu nutzen, muß mit @{ } dereferenziert werden.

Das ging jetzt ziemlich schnell, deshalb noch ein paar Beispiele:

Zur Adresse soll eine Liste von Autoren und deren Büchern zugefügt werden.
Die Autoren werden in einem Hash gespeichert. Die Schlüssel sind die Namen der Autoren, die Werte sind Referenzen auf Listen mit Buchtiteln. Will man das Hash der Autoren separat aufbauen, ergibt sich:

%autoren = ( Tolkien => [ 'Das Silmarillion', 'Nachrichten aus Mittelerde' ], Pratchett => [; 'Wachen, Wachen!', 'Tönerne Füße' ] )
Eine Referenz auf dieses Hash kann man dann zur Adresse zufügen:
$adresse{Bernd}->{Autoren} = \%autoren;
Ein neues Buch von Tolkien wird mit:
push @{$autoren{Tolkien}}, 'Der Kleine Hobbit';
an die Liste angefügt.


Wie kann ich mir eine komplexe Datenstruktur ansehen?

Das Modul Data::Dumper liefert eine Function Dumper. Dieser Funktion übergibt man eine Referenz, Dumper erzeugt Perlcode, der die gesamte Struktur wiedergibt:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my %adresse; $adresse{Bernd} = { Name => 'Bernd Dulfer', Str => 'Kapellenstr. 1', EMail => 'bdulfer@sybase.com', Buecher => [ 'Der Herr der Ringe', 'Per Anhalter durch die Galaxie', 'Die Farben der Magie' ] }; $adresse{Bernd}->{Buecher}->[3] = 'G&ouml;del, Escher, Bach - Ein +endlos geflochtenes Band'; push @{$adresse{Bernd}->{Buecher}}, 'Die Eissegler von Tran-ky-ky' +; $adresse{Bernd}->{Autoren}->{Tolkien} = ['Silmarillion', 'Unfinish +ed Tales']; print Dumper(\%adresse);
Interessant ist hier die Zeile, in mit ['Silmarillion', 'Unfinished Tales'].
Hier wird 'on the fly' ein neues Element 'Autoren' erzeugt. Dies enthält eine Referenz auf ein anonymes Hash mit dem Element 'Tolkien'.
Diesem wiederum wird ein anonymes Array zugewiesen.

Die Ausgabe des Programmes:
$VAR1 = { 'Bernd' => { 'Autoren' => { 'Tolkien' => [ 'Silmarillion', 'Unfinished Tales +' ] }, 'Buecher' => [ 'Der Herr der Ringe', 'Per Anhalter durch die Galaxie' +, 'Die Farben der Magie', 'G&ouml;del, Escher, Bach - Ein +endlos geflochtenes Band', 'Die Eissegler von Tran-ky-ky' ], 'EMail' => 'bdulfer@sybase.com', 'Str' => 'Kapellenstr. 1', 'Name' => 'Bernd Dulfer' } };
Eine ähnliche Anzeige erhält man auch wenn man den Tk-Debugger verwendet.

Wie kann ich Daten aus einer Datei in eine komplexe Datenstruktur laden?

Nehmen wir an, wir haben folgende Datei:

#K&uuml;rzel Name Str EMail Buec +her Bernd |Bernd Dulfer |Kapellenstr. 1 |bdulfer@sybase.com |(Der Her +r der Ringe|Per Anhalter ...|Die Farben der Magie) Bilbo |Bilbo Beutlin |Beutelsend | |(Das Rot +e Buch des Hobbits)
Folgender Code erzeugt aus den Daten den uns bekannten Hash:
1: my %adressen; 2: 3: open ADRESSEN, 'adressen.dat' or die "Konnte Datei adressen.dat + nicht &ouml;ffnen: $!\n"; 4: while (<ADRESSEN>) { 5: next if /^#/; 6: my ($kuerzel, $name, $str, $email, $buecher) = split(/\s*\|/, + $_, 5); 7: $adressen{$kuerzel} = { 8: Name => $name, 9: Str => $str, 10: EMail => $email 11: }; 12: $buecher =~ tr/()//d; 13: push @{$adressen{$kuerzel}->{Buecher}}, split /\|/, $buecher; 14: } 15: close ADRESSEN;
Zeile 1: Hash %adressen wird deklariert.
Zeile 3: Datei adressen.dat wird zum Lesen geöffnet.
Zeile 4: Die Daten werden Zeile für Zeile gelesen. Jede Zeile wird automatisch in $_ abgelegt.
Zeile 5: Kommentare werden ignoriert.
Zeile 6: Zeile wird in fünf Stücke aufgespalten, diese werden den entsprechenden Variablen zugewiesen.
Zeile 7: Ein anonymes Hash mit den Schlüsseln Name, Str und EMail wird erzeugt und eine Referenz darauf im Hash %adressen mit dem Schlüssel $kuerzel abgelegt.
Zeile 12: Die Klammern werden aus der Bücherliste entfernt.
Zeile 13: Ein anonymes Array wird erzeugt und eine Referenz darauf im Hash %adressen abgelegt. Die Bücher werden aufgespalten und in das anonyme Array eingefügt.
Zeile 15: Die Datei wird geschlossen.

Dies ist nur ein Beispiel, diverse Dinge wurden hier nicht berücksichtigt, z.B. könnte ein Buchtitel Klammern enthalten usw.

Wie kann ich durch Arrays und Hashes in Datenstrukturen iterieren?

Alle Bücher einer Adresse:

foreach (@{$adresse{Bernd}->{Buecher}}) { print $_, "\n"; }
Alle Autoren mit deren Büchern:
foreach (keys %{$adresse{Bernd}->{Autoren}}) { print $_, "\n"; foreach (@{$adresse{Bernd}->{Autoren}->{$_}}) { print "\t", $_, "\n"; } }

Wofür braucht man Referenzen auf Subroutines?

Referenzen auf Subroutines braucht man für verschiedene Dinge:

Callbacks:
    In einem Modul soll eine Subroutine des Programmes ausgeführt werden, ohne daß das Modul weiß, wie die Subroutine heißt.
    Dem Modul wird eine Referenz auf die Subroutine übergeben. So kann diese ausgeführt werden:

Package Mod; my $callback; sub set_callback { $callback = shift; } sub do_something { do_this(); do_that(); &$callback; do_something_else(); } . . . 1; __END__ #!/usr/bin/perl use strict; use warnings; use Mod; Mod::set_callback(\&print_rubbish); Mod::do_something(); sub print_rubbish ( print "Rubbish!\n"; }
Tk Programmierer kennen die Verwendung von Callbacks besonders gut, da Events (Mouse Click, Button, ...) an Callbacks übergeben werden.

Dispatch Listen:
   Um größere if/elsif/else Konstruktionen zu vermeiden, werden die Code Stücke in Subroutines gepackt.
   Referenzen auf die Subroutines werden in einem Hash gespeichert, dessen Keys den Bedingungen entsprechen.

%dispatch = ( insert => \&insert, update => \&update, delete => \&delete ) $dispatch{$token}->($query);


Update: changed <pre> to <code>
Update: Falsche Klammern an zwei Stellen, gefunden von petral
Update: diverse Tippfehler
Update: Link zu (PEGS) ersetzt

Replies are listed 'Best First'.
Re: (lang: de) Referenzen
by Rudif (Hermit) on Nov 27, 2001 at 04:36 UTC
    Endlich wird es alles klar. Danke.

    Rudif

Re: (lang: de) Referenzen
by Sergej (Initiate) on Jul 27, 2006 at 21:03 UTC
    Sehr guter Tutorial das beste was ich zu diesem Thema gelesen habe!
Re: (lang: de) Referenzen
by Spenser (Friar) on Dec 14, 2001 at 05:47 UTC
    Sehr gut!