Namensräume und Gültigkeitsbereiche von Variablen in Perl

Allgemeines

Wenn Sie mit Perl beginnen, müssen Sie wissen, wie in dieser Programmiersprache Variablen deklariert werden können und welche unterschiedlichen Gültigkeitsregeln in Abhängigkeit von der Deklaration gelten. Es ist nicht das Ziel von Perl, eine einfache und übersichtliche Sprache zu sein. Das Konzept war vielmehr immer, viele Möglichkeiten und Stile zuzulassen. Daher mag das Variablenkonzept von Perl für einen Anfänger unübersichtlich und konzeptionslos erscheinen, doch das ist es nicht.

Wenn Sie mit Perl beginnen, sollten Sie sich drei Grundregeln verinnerlichen:

  1. Es gibt in Perl zwei völlig verschiedene Variablen-Konzeptionen: Packagevariablen und lexikalische Variablen.
  2. Das Vermischen beider Konzepte birgt Gefahren, denen Sie sich nicht aussetzen sollte.
  3. Verwenden Sie stets lexikalische Variablen und lassen Sie alle Deklarationen vom strict-Pragma überwachen.

Namensräume, Symboltabellen und Gültigkeitsbereich

Zunächst muss unterschieden werden zwischen Namensraum (Namespace) und Gültigkeitsbereich (Scope). Während Namespace der Namensraum ist, über den eine Variable "identifiziert" wird, bedeutet Scope zum Einen, wo eine Variable gültig ist und zum Anderen nach welchen Regeln (local vs. my, my vs. our).

Symboltabellen

Perl führt für jeden Namensraum eine Symboltabelle, die nichts anderes als ein Hash ist, in dem die Symbole (also auch die Namen aller Variablen) als Schlüssel und ihr Inhalt als Wert gespeichert sind. Auf Symboltabellen kann man nach der Syntax %name:: zugreifen, wobei name natürlich durch den Namen der betroffenen Symboltabelle zu ersetzen ist. Jedes Script bildet zunächst nur einen Namensraum main, der für das gesamte Script gilt. Weitere Namensräume beginnt man durch die package-Anweisung, gefolgt vom gewünschten Namen. Aus diesem Grund werden die Begriffe Namensraum und Package oft auch als Synonyme verwendet.

Codebeispiel:

# wir befinden uns aktuell in 'main'

package First;
# nun befinden wir uns in 'First'

package Second;
# wir haben 'First' verlassen und befinden uns in 'Second'

package main;
# wir haben 'Second' verlassen und befinden uns wieder in 'main'

Gültigkeitsbereich

Die Gültigkeit von Variablen (der Scope) richtet sich danach, welche Art von Variablen verwendet wird: Packagevariablen oder lexikalische Variablen. Während Packagevariablen in der für den betroffenen Namensraum gültigen Symboltabelle gespeichert werden, sind es lexikalische Variablen nicht. Sie werden für den Block, zu dem sie gehören, in internen Arrays gespeichert, den sog. Scratchpads.

Packagevariablen und Global Scope

Variablen, die über die Symboltabelle einem Namensraum bzw. Package zugeordnet sind, nennt man Packagevariablen. Diese Variablen sind immer global, sodass sie sowohl von jeder Stelle des Scripts aus als auch von außerhalb des Scriptes angesprochen werden können. Man nennt sie deshalb auch öffentliche Variablen. Außerhalb des Namensraumes, zu dem sie gehören, müssen sie durch Voranstellen der Bezeichnung des Namensraumes gefolgt von zwei Doppelpunkten notiert werden. Lassen Sie die Angabe des Namensraumes weg, greifen Sie auf die Variable im aktuellen Namensraum zu.

Codebeispiel 1

package Feuerstein;
$Name = 'Fred';             # erzeugt $Feuerstein::Name

package Geroellheimer;
$Name = 'Barney';           # erzeugt $Geroellheimer::Name

package Ausgabe;
print $Feuerstein::Name;    # Fred
print $Geroellheimer::Name; # Barney

Erläuterungen:

Alle im Beispielscript deklarierten Variablen sind global. Als erstes wird im Namensraum Feuerstein die Variable $Name deklariert. Dann folgt ein weiterer Namensraum Geroellheimer, dort wird ebenfalls eine Variable $Name deklariert. Da bei beiden Variablendeklarationen kein Namensraum angegeben wurde, beziehen sie sich jeweils auf das aktuelle Package, wie man in der Kontrollausgabe im Package Ausgabe erkennen kann.

Codebeispiel 2

$Mann = undef;

package Feuerstein;
$main::Mann = 'Fred';
$Frau = 'Wilma';
$Kind = 'Pebbles';

package main;
print "$Mann\n";
print "$Feuerstein::Frau\n";
print "$Feuerstein::Kind";

Erläuterungen:

Zunächst wird mit $Mann eine globale Variable angelegt. Mit der folgenden package-Anweisung wird ein neuer Namensraum Feuerstein angelegt, darin wird zuerst der Variablen $main::Mann der Wert 'Fred' zugewiesen, danach werden die Variablen $Frau und $Kind deklariert. Durch die Anweisung package main; verlässt das Script den Namensraum Feuerstein und kehrt in den globalen Namensraum main zurück. Dort werden alle im Script definierten Package-Variablen zur Kontrolle ausgegeben.

Beachten Sie:

Das Arbeiten mit globalen Variablen ist sehr fehleranfällig und erschwert besonders Anfängern die Suche nach Problemen. Da die Modifikation globaler Variablen an jeder beliebigen Stelle im Script möglich ist, können insbesondere beim Weglassen der Namespace-Angabe unerwartete Ergebnisse auftreten. Abhilfe schafft hier das Verwenden lexikalischer Variablen.

Dynamic Scope

Um globale Variablen innerhalb eines Anweisungsblocks temporär manipulieren zu können, bietet Perl den sog. Dynamic Scope. Der hierfür vorgesehene local-Operator legt eine Sicherungskopie des Wertes einer globalen Variable an und setzt sie dann auf undef, somit kann die Variable innerhalb des umschließenden Anweisungsblocks beliebig verändert werden. Endet der Block, wird die Variable wieder auf ihren gesicherten Wert zurückgesetzt.

Codebeispiel

$foo = 'global';

sub bar {
  local $foo;
  $foo = 'lokal';
  print $foo;  # lokal
}

bar();
print $foo;  # global

Erläuterungen:

Es wird eine globale Variable $foo deklariert und ihr der String 'global' zugewiesen. Innerhalb der Subroutine bar wird durch die Anweisung local $foo; der globale Wert gesichert, $foo hat innerhalb der Subroutine nun keinen definierten Wert mehr. Im nächsten Schritt wird $foo der String 'lokal' zugewiesen und die Variable zur Kontrolle ausgegeben. Schlussendlich wird die Subroutine bar(); aufgerufen und gibt erwartungsgemäß 'lokal' aus, während die letzte Anweisung den global definierten Wert 'global' ausgibt.

Der Dynamic Scope ist in folgenden Anwendungsfällen sinnvoll:

  1. Sie müssen einer von Perl vordefinierten globalen Variable einen temporären Wert geben.
  2. Sie möchten ein lokales Datei- oder Verzeichnishandle bzw. eine lokale Funktion anlegen.
  3. Sie möchten temporär nur ein Element eines Arrays oder Hashs verändern.

Beachten Sie:

Der local-Operator legt keine neue Variable an, sondern sichert den Zustand einer globalen Variable für einen bestimmten Anweisungsblock. Auf lexikalische Variablen können Sie local nicht anwenden.

Lexical Scope

Der am wenigsten fehleranfällige Weg, Variablen zu deklarieren, ist der Lexical Scope. Hierzu stellt man der Variablendeklaration das Schlüsselwort my voran. Anders als bei den bisher beschriebenen Gültigkeitsregeln richtet sich der Gültigkeitsbereich von lexikalischen Variablen nicht nach dem Namensraum oder Package, sondern danach, wo die Variable deklariert wird. Lexikalische Variablen sind ab ihrer Deklaration im umschließenden und allen untergeordneten Anweisungsblöcken verfügbar, nicht jedoch außerhalb dieses Blockes, weshalb sie auch private Variablen genannt werden. Wenn Sie lexikalische Variablen nicht innerhalb eines eigenen Anweisungsblocks, sondern z.B. am Anfang eines Scriptes deklarieren, sind sie zwar scriptweit gültig, aber dennoch nicht global im Sinne von öffentlich, da man von außerhalb des Scriptes keinen Zugriff auf sie hat.

Codebeispiel

my $Vornamen = 'Fred';
print "$Vornamen Feuerstein\n";

Hochzeit();

print "$Vornamen Feuerstein\n";
print "Trauzeuge war: $Trauzeuge";

sub Hochzeit {
  $Vornamen .= ' und Wilma';
  my $Trauzeuge = 'Barney Geroellheimer';
}

Erläuterungen:

Die Variable $Vornamen ist nicht innerhalb eines Anweisungsblockes notiert und deshalb im gesamten Script verfügbar. Daher kann sie innerhalb der Subroutine Hochzeit geändert werden, diese Änderung wirkt ab dem Zeitpunkt, ab dem die Subroutine aufgerufen wurde. Zunächst wird nur 'Fred' ausgegeben, nach Aufruf von Hochzeit(); wird der veränderte String 'Fred und Wilma' ausgegeben. Auf die Variable $Trauzeuge hat man außerhalb von Hochzeit keinen Zugriff, da sie nur innerhalb dieser Subroutine Gültigkeit hat. Perl quittiert hier den Zugriff mit einer Warnung.

Beachten Sie:

Der Lexical Scope wird gerade für Anfänger als der am wenigsten fehleranfällige Weg angesehen, Variablen zu deklarieren. Um innerhalb eines Scriptes durchgängig lexikalische Variablen zu verwenden, bietet Perl Ihnen Unterstützung durch das strict-Pragma an. Es überwacht sämtliche Variablendeklarationen und lässt keinen anderen als den Lexical Scope zu.

Globale Variablen im Lexical Scope

Um auch im lexical scope globale Variablen deklarieren zu können, wurde mit Perl 5.6.0 der our-Operator eingeführt, der seinerseits use vars ersetzte und erweiterte.

Codebeispiel

my $Vornamen = 'Fred';
print "$Vornamen Feuerstein\n";

Hochzeit();

print "$Vornamen Feuerstein\n";
print "Trauzeuge war: $Trauzeuge";

sub Hochzeit {
  $Vornamen .= ' und Wilma';
  our $Trauzeuge = 'Barney Geroellheimer';
}

Erläuterungen:

Innerhalb der Subroutine Hochzeit wird die Variable $Trauzeuge als global im lexikalischen Scope deklariert. Im Ergebnis kann auf sie zugegriffen werden, als ob sie wie die Variable $Vornamen auf der obersten Scriptebene deklariert worden wäre.

Variablen, die mittels our deklariert wurden, sind an den Namensraum gebunden, in dem sie deklariert werden. Innerhalb dieses Namensraumes verhalten sie sich wie lexikalische Variablen, außerhalb je nach Art des Zugriffs wie lexikalische oder wie Packagevariablen:

Codebeispiel

package Feuerstein;
our $Nachname = 'Feuerstein';

package Geroellheimer;
our $Nachname = 'Geroellheimer';

package main;
print $Nachname;
print $Feuerstein::Nachname;
print $Geroellheimer::Nachname;

Erläuterungen:

Das Script definiert mittels der package-Anweisung zwei Namensräume: Feuerstein und Geroellheimer. Innerhalb dieser Namensräume wird jeweils eine Variable $Nachname mit den jeweiligen Nachnamen definiert. Zurück im Namensraum main wird zunächst im lexical scope auf $Nachname zugegriffen und gibt die letzte Modifikation aus, nämlich den String 'Geroellheimer'. Danach wird nach den Regeln für Packagevariablen auf $Nachname zugegriffen und man erhält als Ergebnis die innerhalb des jeweiligen Namensraumes definierten Werte für $Nachname, nämlich 'Feuerstein' und 'Geroellheimer'.

Beachten Sie:

Der our-Operator wurde aus Kompatibilitätsgründen eingeführt, um die Vor- und Nachteile des Global Scope und des Lexical Scope zu verbinden. Da für solche Variablen eigene Regeln gelten, sollten Sie our mit Vorsicht einsetzen.