Referenzen mit Perl

Joachim on 15.10.2015

Referenzen sind Verweise auf Datenobjekte in Perl. Referenzen können auf Variablen zeigen, aber auch auf Subroutinen, Filehandles oder sogar auf Werte. Nur mit Referenzen ist es in Perl möglich mit komplexen Datenstrukturen bzw. mit verschachtelten Datenstrukturen zu arbeiten.
Referenzen sehen auf den ersten Blick wie Pointer in C aus, jedoch nur auf den ersten Blick. Der große Unterschied zu Pointern ist die Tatsache, dass Referenzen nur Verweise sind, aber keine Adressen wie in C, mit denen man rechnen kann. Das einzige, was man mit Referenzen machen kann, ist, sie zu dereferenzieren und auf den Wert oder die Struktur zuzugreifen, auf die die Referenz verweist.

Überblick

  • Referenzen gibt es in Perl seit Version 5.
  • Mit ihnen sind überhaupt geschachtelte Strukturen möglich geworden.
  • Sie bilden auch die Grundlage der Objektorientierten Programmierung in Perl, denn auch Objekte sind spezielle Referenzen.

Referenzen können in Perl auf zwei Arten erzeugt werden:

  1. durch '\'-Operator, angewandt auf eine Variable oder eine Funktion:
      $scalar = "Dies ist ein String";
    	$scalarref = \$scalar;
    	@array = qw(1 2 3 4 5 6 7 8 9 10 Start);
    	$arrayref = \@array;
    	%hash = ("franz" => 23,  "hans" => 20 );
    	$hashref = \%hash;
    	sub funk1{
    	  print "Dies ist eine Funktion\n";
    	}
    	$funkref = \&funk1;
    	
  2. direkt ohne Nutzung 'normaler' Variablen, sondern spezieller Operatoren
    	$scalarref = \"Dies ist ein String";
    	$arrayref = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "Start"];
    	$hashref = {"franz" => 23,  "hans" => 20 };
    	$funkref = sub { print "Dies ist eine Funktion\n"; }
    	

Referenzen selbst sind skalare Variable (erkennbar am '$').

Benutzung von Referenzen

Um auf ein 'Objekt', also eine skalare Variable, ein Array oder Hash zuzugreifen, auf das eine Referenz zeigt, ist das die Variablen des entsprechenden Typs kennzeichnende Sonderzeichen ('$','@','%') der Referenzvariablen vorzustellen.
Beispiele:

print "Scalar = $$scalarref \n";
print "Array  = @$arrayref  \n";
foreach $key (keys %$hashref) {
  print "Alter von ",$key," ist ",$$hashref{$key},".\n";
}
&$funkref();
${$arrayref}[1] = 12;
${$hash}{"karl"} = 57;

Bei Arrays und Hashes empfiehlt sich für den Zugriff auf Komponenten der Dereferenzierungsoperator '->'. Dadurch ersparen wir uns Klammern, machen die Programme leichter lesbar und verwenden eine aus anderen objektorientieren Sprachen bekannte Notation.
Beispiele:

${$arrayref}[1] = 12;     # alte Version
$arrayref->[1]  = 13;     # Nutzung von ->
${$hash}{"karl"} = 57;    # alte Version
$hash->{"karl"}  = 57;    # Nutzung von ->

Beispiel: Datenbank mit Adressen und Vornamen

In diesem Beispiel untersuchen wir eine Datenbank, in der Adressen aufgeführt werden. Neben den Adressen beinhaltet die Datenbank auch die jeweiligen Vornamen die unter dieser Adresse geführt werden.
In unserem Beispiel werden wir:

  • die Adressdaten aus einer Datei in eine komplexe verschachtelte Datenstruktur zu lesen,
  • nach der Eingabe eines Suchstrings zu fragen und
  • für einen gegebenen Suchstring die Daten der gesuchten Adresse auszugeben.

Die Daten, die wir in diesem Beispiel betrachten, umfassen den Name, Straße, PLZ und Ort sowie eine Liste aller Vornamen die unter dieser Adresse erfasst sind. Die Adressdaten sind in einer externen Datei gespeichert, die pro Adresse zwei Zeilen aufweist:

Mustermann,Musterstr. 47,4711,Musterhausen
Hans:Gerlinde:Erwin:Siegfried

Die erste Zeile besteht aus den Adressdaten, diese sind durch Komma getrennt, die zweite Zeile enthält alle Vornamen der Mitbewohner, diese sind durch Doppelpunkt getrennt. Die Struktur, in die wir diese Informationen lesen, ist ein Hash von Hashes mit einem verschachtelten Array. Der oberste Hash verwendet als Schlüssel den Nachnamen der Adresse. Die weiteren Adressdaten sind in einem verschachtelten Hash mit den Schlüsseln AS, AP, AO und vn. Der Wert für den Schlüssel vn ist wiederum ein Array, in dem die einzelnen Vornamen aufgeführt sind.

play_button Abbildung: Verschachtelte Datenstruktur

Beispiel: Perl-Code

 

#!/perl/bin perl
=begin nd
		TITLE: Beispiel Hashes von Hashes von Arrays
         FILE: selectAdress.pl
        USAGE: ./selectAdress.pl  
  DESCRIPTION: 
    PARAMETER: ---
        NOTES: ---
       AUTHOR: Joachim Nyenhuis
      VERSION: 1.0
      CREATED: 13.10.2015 19:19:58
     		Topic: History
	13.10.2015 - Joachim Nyenhuis - Beginn Implementierung
=cut
use strict;
use utf8;
my $adressdb = "adressen.txt";           # Name der Adressdatenbank
my %adressen = ();                       # Hash der Adressen, Nachname als Schlüssel
&read_adressen();
&search_name();
#===  FUNCTION  ================================================================
#         NAME: read_adressen
#      PURPOSE: Datenstruktur anlegen
#  Description: Die Funktion read_adressen legt die Daten in einem 
#               verschachtelten Hash ab.
#===============================================================================
sub read_adressen {
    my $in = '';                # temp. Eingabezeile
    my ($an,$as,$ap,$ao);       # Nachname, Strasse, PLZ, Ort
    my %adresse = ();           # temp. Adress-Hash
    open(FILE, $adressdb) or 
            die "Datenbank ($adressdb) konnte nicht geoeffnet werden: $!\n";
    while () {
        # Name und Daten in ersten Zeile werden eingelesen
        chomp($in = <FILE>);
        if ($in) {
						# Daten werden in Einzelbestandteile zerlegt
            ($an,$as,$ap,$ao) = split(',',$in);
						# Daten werden in den temporären Hash abgelegt
            $adresse{AS} = $as;
            $adresse{AP} = $ap;
            $adresse{AO} = $ao;
 					 	# Liste der Vornamen in zweiter Zeile wird eingelesen
            chomp($in = <FILE>);
            if ($in) {
								# Liste der Vornamen wird in dem temporären Array @vornamen gespeichert!
                my @vornamen = split(':',$in);
								# Referenz des temporären Array wird in unserem temporären Hash %adresse eingefügt.
								# Es ist zu beachten das bei jedem Durchlauf der while-Schleife einen neuen
								# temporären @vornamen-Array (deklariert mit my) erhalten, so dass wir das Problem, 
								# jedesmal die gleiche die gleiche Speicherposition zu referenzieren,  umgehen.
                $adresse{vn} = \@vornamen;
            }else{ 
							print "keine Vornamen!";
						}
            # Die Referenz auf das Adress-Hash in das äußere Adressen-Hash eintragen
						# Anonymer Hash-Konstruktor
            $adressen{$an} = { %adresse };
        }else{ 
					last; 
				}   # Ende von DB
    }
}
#===  FUNCTION  ================================================================
#         NAME: search_name
#      PURPOSE: Ausgabe Adresse mit Vornamen
#  DESCRIPTION: 
#===============================================================================
sub search_name {
  my $input = '';
  my $matched = 0;
  print "Geben Sie einen Nachnamen ein: ";
  chomp($input = <>);
  # Deferenzierung der Referenzen, um an die benötigten Daten zu gelangen.
  # Da wir keine eigentliche Schleifenvariable benutzen, speichert Perl die
  # Schlüssel in der Variablen $_.
  foreach (keys %adressen) {
    # Mustervergleich auf Übereinstimmung. Da wir nur an der ersten Üereinstimmung
    # interessiert sind setzen wir die Variable matched auf 1 bei der ersten Übereinstimmung.
    if (/$input/i  and !$matched) {
      $matched = 1;
      # Falls Übereinstimmung Deferenzierung, um den Inhalt des Hashes zu erhalten.
      my $ref = $adressen{$_};
      print "$_, $ref->{AS} $ref->{AP} $ref->{AO}\n";
      my $vornamen = '';
      # Erklärung zu @{$ref->{vn}}
      # Zur Erinnerung: in $ref befindet sich die Referenz auf einen Hash. Der Ausdruck 
      # $ref->{vn} derefenziert diese Referenz und liefert den Wert zurück,  der durch den Schlüssel vn gegeben ist.
      # Dieser Wert ist wiederum eine Referenz, diesmal jedoch eine Referenz auf ein Array. Um
      # diese Referenz zu dereferenzieren und ein tatsächliches Array zu erhalten,  das man mit der 
      # foreach-Schleife durchlaufen kann, bedarf es der Blocksyntax zu Dereferenzieren: @{}.
      foreach $vornamen (@{$ref->{vn}}) {
        print "   $vornamen\n";
      }
    }
  }
  if (!$matched) {
    print "Der Nachname $input wurde nicht gefunden.\n";
  }
}

 

Hinweise:

Skalarvariable
Eine skalare Variable ist im Kontext von Programmiersprachen eine Variable, die einen einzelnen Wert speichert. Der Begriff ist angelehnt an den mathematischen Skalar.

ref-Funktion
Mit der ref-Funktion stellen wir fest von welcher Art die Daten sind. Die ref-Funktion übernimmt einen Skalar als Argument. Ist der Skalar keine Referenz, das heißt, ist er ein String oder eine Zahl, dann liefert ref einen NULL-String zurück. Andernfalls wird ein String geliefert, mit der Art der referenzierten Daten.
Mögliche Werte sind:

  • REF = Eine andere Referenz
  • SCALAR = Einen skalaren Wert
  • ARRAY = Ein Array
  • HASH = Ein Hash
  • CODE = Eine Subroutine
  • GLOB = Ein Typeglob (Typenplatzhalter)
  • NULL = Keine Referenz

Verschachtelte Datenstrukturen
Eine beliebige Kombination von Listen, Array und Hashes - nennen wir verschachtelte Datenstrukturen.

Anonyme Daten
Anonym bedeutet "ohne einen Namen". Anonyme Daten beziehen sich speziell in Perl auf Daten (normalerweise Arrays oder Hashes, aber auch Subroutinen), auf die nur über eine Referenz zugegriffen werden kann, also Daten, die mit keinem Variablennamen verbunden sind.

Links:
Einführung in Perl - Uni Kiel
Perl-Guide - PerlBoard.de

Zurück