| Arrays und Hashes können große Mengen an Daten enthalten. Insbesondere mit Hashes werden in Perl sehr komplexe Strukturen abgebildet. Bei der Verarbeitung vermeidet man es, derartige Strukturen zu verschieben oder zu kopieren: Man arbeitet mit den Adressen der jeweiligen Elemente. | Die Arbeit mit Adressen (Referenzen, Pointern) ist komplizierter als jene mit einfachen Variablen. Sie erfordert Erfahrung und Disziplin - Andernfalls treten unangenehme Fehler auf... |
Perl |
Scripts für CGI und Systemverwaltung (Arrays und Hashes ) |
| Variable & Pointer | Pointer auf Skalare, Arrays und Hashes |
| Array → Sub | Übergabe von Arrays und Hashes an Unterprogramme |
| Sub → Array | Rückgabe von Arrays aus Unterprogrammen |
| Array | Mehr-dimensionale Arrays |
| Hash | Mehr-dimensionale Hashes |
| Analyse | Demonstration komplexer Arrays und Hashes |
Übergabe von Arrays und Hashes an Unterprogramme |
|
| In diesem Kapitel wird die Anwendung von Referenzen (Adressen, Pointern) bei der Übergabe von Arrays und Hashes an Unter-Programme vorgestellt. | Diese Anwendung hat große praktische Bedeutung. |
Globale ArraysAlle in einem Perl (Haupt)-Programm definierten Arrays und Hashes sind global: Man kann sie ohne weitere Maßnahme in allen Unter-Programmen der gleichen Datei verwenden. Im Beispiel rechts kann das Unter-Programm subtest alle Werte des Arrays @ara ausgeben, obwohl dieses Array nicht an das Unter-Programm übergeben wurde.● Diese Methode ist unübersichtlich und begünstigt das Auftreten von Fehlern. Sie sollte - wenn überhaupt - nur in winzigen Programmen verwendet werden. |
# Haupt-Programm:
@ara = (99..101);subar(); exit 0; # Unter-Programm:
sub subar{
foreach $v(@ara) {
}
print "$v \n ";
}
|
Globale HashesAlle in einem Perl (Haupt)-Programm definierten Hashes sind global. Das Unter-Programm subha gibt ohne Übergabe alle Elemente des im Haupt-Programm definierten Hash %ha aus.● Diese Methode ist unübersichtlich und gegen Fehler anfällig, und sollte deshalb nicht verwendet werden. ● Eine Ausnahme stellen die Super-globalen Hashes dar, z.B. das in allen Perl-Programmen ohne Deklaration verfügbare Hash %ENV der → Umgebungs-Daten: Das Unter-Programm subenv gibt alle Elemente dieses Hash aus, obwohl es weder im Programm definiert noch an das Sub übergeben wurde. |
# Haupt-Programm:
%ha = ('r'=>12,'g'=>34,'b'=>45);subha(); subenv(); exit 0; # Unter-Programme:
sub subha{
while (($k,$v) = each %ha) {
}print "ha: $k = $v \n ";
}
sub subenv{
while (($k,$v) = each %ENV) {
}
print "env: $k = $v \n ";
}
|
Übergabe einer Array-AdresseIm Haupt-Programm des Beispiels rechts werden die beiden Arrays @ara, @arb definiert.Danach wird 2mal das Unter-Programm subap aufgerufen. Als Argument wird jeweils die Adresse eines Arrays übergeben. Das Unter-Programm subap erhält als Argument $ap einen Pointer auf ein Array. Um das jeweilige Array zu verwenden, wird der Pointer in @{} eingeschlossen. • An das Unterprogramm wird nur eine einzige Variable übergeben. Damit wird ein Array beliebiger (!) Größe zugänglich, ohne dass dessen Werte kopiert oder im Speicher verschoben werden. • Das Sub ist nicht an ein bestimmtes Array gebunden, sondern kann zur Ausgabe jedes beliebigen Arrays verwendet werden. • Es verwendet ausschließlich private Variable und ist daher übersichtlich und gegen Fehler unempfindlich. • Man kann das Sub ohne Änderung in jedes andere Perl Script-Programm kopieren. |
@ara = (99..101);
@arb = (10..15); subap(\@ara); subap(\@arb); exit 0; # Unter-Programm:
sub subap{
my($ap)=@_;
}
foreach $v(@{$ap}) { print "$v \n ";
}
|
|
Änderung von Array-Daten
Wenn ein Unter-Programm die Adresse eines Arrays erhält, dann greift es auf die
Original-Daten zu, nicht etwa auf eine Kopie der Array-Daten. Daher wirkt sich jede
Änderung auf die Original-Daten aus ! - Das Beispiel rechts ändert den
Wert von Element [1] jedes als Pointer übergebenen
Arrays auf 999.
|
sub subap2{
my($ap)=@_;
}
${$ap}[1] = 999; |
Übergabe einer Hash-AdresseIm Haupt-Programm des Beispiels rechts werden die beiden Hashes %ha, %hb definiert.Danach wird 2mal das Unter-Programm subhp aufgerufen. Als Argument wird jeweils die Adresse eines Hashes übergeben. Das Unter-Programm subhp erhält als Argument $hp einen Pointer auf ein Hash. Um das jeweilige Hash zu verwenden, wird der Pointer in %{} eingeschlossen. • Alle im Absatz oberhalb zu Arrays genannten Vorteile sind auch für Hashes nutzbar. • Diese Methode hat große praktische Bedeutung, da komplexe Strukturen in Perl meist als Hashes abgebildet werden. In diesem Fall wird die Methode meist mit mehr-dimensionalen Hashes angewendet. |
%ha = ('r'=>23,'g'=>34,'b'=>45);
%hb = ('vn'=>'Anna','zn'=>'Zupal'); subhp(\%ha); subhp(\%hb); exit 0; # Unter-Programm:
sub subhp{
my($hp)=@_;
}
while (($k,$v) = each %{$hp} { print "key=$k, value=$v\n";
}
|
|
Änderung von Hash-Daten
Wenn ein Unter-Programm die Adresse eines Hash erhält, dann greift es auf
die Original-Daten zu. Daher wirkt sich jede Änderung der Daten auf das
Original-Hash aus ! - Das Beispiel rechts ändert den Wert von
Element 'serr' jedes als Pointer übergebenen Hashes
auf 'gutt'. Wenn ein solches Element nicht existiert, wird es zusätzlich angelegt.
|
sub subhp2{
my($hp)=@_;
}
${$hp}['serr'] = 'gutt'; |
Mehr-dimensionale Hashes |
|
|
Die Elemente eines Hash können ebenso wie jene eines Arrays aus Skalaren und/oder
Pointern bestehen. Pointer-Elemente führen zu weiteren Arrays oder Hashes. Das komplexe Hash %h2 des Beispiels enthält als Elemente die beiden Skalare $h2{'x'} und $h2{'z'} sowie im Element $h1{'rgb'} das gesamte Hash %h1 mit seinen 3 skalaren Elementen. |
# Einfaches Hash:
%h1=('r'=>123,'g'=>135,'b'=>246);# Komplexes Hash:
%h2=('x'=>'abc','rgb'=>\%h1,'z'=>'rst');
|
|
Alternative Darstellung: Anonyme Hashes
Wenn nur der Pointer gebraucht wird, nicht jedoch das Hash selbst, dann kann man durch
Einschließen der Element-Werte in {} Klammern einen
Pointer auf ein 'Anonymes Hash' erzeugen.Der erzeugte Pointer $ph1 dient selbst als Element von Hash %h2 |
$ph1 = {'r'=>123,'g'=>135,'b'=>246};
%h2=('x'=>'abc','rgb'=>$ph1,'z'=>'rst'); |
|
Alternative Darstellung: Anonyme Hashes
Das gesamte Hash wird in einer einzigen Anweisung angegeben.
Das darin enthaltene Sub-Hash wird anonym angegeben.
|
%h2 = ('x'=>'abc',
'rgb'=>{'r'=>123,'g'=>135,'b'=>246},
'z'=>'rst'); |
Adressierung der Hash-Elemente:Ebenso wie bei der Adressierung komplexer Arrays kann man die -> Pfeile zwischen aufeinander folgenden Schlüsseln (keys) weglassen. |
# Lesen:
$v = $h2{'rgb'}{'g'};
# ergibt
$h2{'rgb'}{'r'} = 123;
# $v = 135 # Schreiben: |
Übergabe an ein UnterprogrammHashes sollten prinzipiell nur als Pointer an Unterprogramme übergeben werden. Das gilt auch für komplexe Hashes.Im Beispiel wird ein Pointer auf das Hash %h2 als Argument an das Sub htest übergeben. Im Sub htest wird das Argument als Pointer $hp benutzt, um damit Zugang zu allen Elementen des Hash zu erhalten. Als Beispiel wird der Wert eines einzelnen Elements gelesen. Wenn dieser Wert geändert wird, wirkt diese Änderung direkt auf das Original-Hash %h2 des aufrufenden Programms. |
Übergabe htest(\%h2);
Unterprogramm:
sub htest{
my($hp)=@_;
}
my $v = ${$hp}{'rgb'}{'r'};
# ergibt
${$hp}{'rgb'}{'r'} = 99;
# $v = 135 # Aenderung: # oder
$hp->{'rgb'}{'r'} = 99;
|
Rückgabe von einem UnterprogrammHashes sollten von Unterprogrammen prinzipiell nur als Pointer an zurückgegeben werden. Das gilt auch für komplexe Hashes mit beliebig vielen Dimensionen.Im Beispiel wird vom Sub make_mh ein mehrdimensionales Hash erzeugt und als Pointer an die Variable $hp zurückgegeben. Als Vereinfachung (!) wird angenommen, dass in diesem Hash nur Elemente aus skalaren Variablen und 1-dimensionalen Hashes enthalten sind. Im Gegensatz zu üblichen Arrays muss man damit rechnen, dass jedes Sub-Hash eine andere Anzahl an Elementen enthält. In der äußeren while-Schleife wird für jedes Hash-Element ein Variablen-Paar $k1 (Key) und $v1 (Wert) zugewiesen. • Wenn der Wert $v1 skalar ist, dann erfolgt eine Ausgabe in der Form 'Schlüssel=Wert'. • Wenn das Element selbst ein Hash ist, dann wird das Wort 'HASH:' ausgegeben. Anschließend wird in der inneren Schleife aus jedem Element des Sub-Hash ein weiteres Variablen-Paar $k2,$v2 zugewiesen und in der gleichen Zeile ausgegeben. • Zuletzt wird noch demonstriert, wie man ein bestimmtes Element (mit bekannten Keys) adressiert. ♣ Real verwendete mehrdimansionale Hashes folgen oft keinem festgelegten Aufbau, sondern können als Elemente Skalare, Arrays und Hashes enthalten, wobei die Sub-Arrays und -Hashes selbst wieder mehrdimensional sein können. Die Verarbeitung erfordert sehr allgemein gehaltene und rekursiv arbeitende Funktionen. |
Hauptprogramm my($hp,$k1,$v1,$k2,$v2);
$hp = make_mh(); while (($k1,$v1) = each %{$hp}) {
print "mh[$k1]=";
}if(ref($v1) eq 'HASH') {
print 'HASH: ';
}
else{print "$v1 \n";}
while (($k2,$v2) = each %{$v1}) { print "$k2=$v2, ";
}print "\n"; my $test=$hp->{'y'}{'r'}; print "test = $test \n"; Unterprogramm:
sub make_mh{
Ausgabe (Achtung - Die Reihenfolge der Hash-Elemente
ist undefiniert !)
my %ha=('r'=>12,'s'=>23);
}
my %mh=('x'=>'abc','y'=>\%ha,'z'=>789); return \%mh;
mh[x]=abc
mh[y]=HASH: r=12, s=23, mh[z]=789 test = 12 |
Pointer-Analyse |
|
|
Dieses Beispiel zeigt zur Demonstration komplexer Strukturen einige Anwendungen der
Funktion analyze_pointer Diese Funktion (nächster Absatz) zerlegt komplexe Strukturen in ihre einzelnen Elemente und zeigt deren Werte an. Die erste Anwendung erfolgt mit der skalaren Variablen $x sowie mit einem Pointer \$x auf ebendiese Variable. Danach folgt die Analyse des einfachen Arrays @aa und des komplexen Arrays @ba Zuletzt wird das einfache Hash %ha und das komplexe Hash %hb analysiert. Letzteres enthält als Elemente Skalare, ein weiteres Hash und ein komplexes Array. Einige Beispiele für den erzeugten Ausgabe-Text finden sie am Ende dieses Kapitels. |
# Scalar
$x=123; analyze_pointer($x,'x'); analyze_pointer(\$x,"\\x"); # Array @aa=(10..12); analyze_pointer(\@aa,'aa'); @ba=('abc',\@aa,'xyz'); analyze_pointer(\@ba,'ba'); # Hash %ha=('r'=>123,'g'=>135,'b'=>246); analyze_pointer(\%ha,'ha'); %hb=('abc'=>'Eva','rst'=>\%ha,'xyz'=>'Zap','test'=>\@ba); analyze_pointer(\%hb,'hb'); |
|
Die Funktion analyze_pointer untersucht ein beliebiges
Argument $arg Der optionale Text $txt dient zur besseren Verständlichkeit. Mit Hilfe der Standard-Funktion ref wird entschieden, ob das Argument $arg ein Pointer oder ein Skalar ist. Wenn das Argument $arg ein Pointer ist, dann wird dessen Typ ausgegeben. Danach werden 3 Fälle genauer analysiert: SCALAR, ARRAY, HASH Alle anderen Pointer-Varianten werden nicht weiter analysiert. Ein Pointer auf SCALAR wird unmittelbar angezeigt. Pointer auf ARRAY oder HASH werden an andere Subs (nächster Absatz) weitergegeben. Wenn das Argument $arg ein Skalar ist, wird dessen Wert angezeigt. |
sub analyze_pointer{
my($arg,$txt)=@_;
}
if(ref $arg) { # $arg is a pointer
my $reftyp=ref($arg);print $txt.' = pointer to '.$reftyp."\n"; if($reftyp eq 'SCALAR'){ print "\${".$txt.'} = '.${$arg}."\n";
}
elsif($reftyp eq 'ARRAY'){
analyze_array_pointer($arg,$txt);
}elsif($reftyp eq 'HASH'){ analyze_hash_pointer($arg,$txt);
}
else{ # $arg is a scalar
print $txt.' = '.$arg."\n";
|
|
Die beiden Subs analyze_array_pointer
und analyze_hash_pointer zerlegen ein Array bzw.
ein Hash in seine Elemente. Jedes dieser Elemente wird der weiteren Analyse durch das Sub analyze_pointer (Absatz oben) unterzogen. Dabei wird es so lange weiter zerlegt, bis zuletzt alle Endpunkte = Skalare erreicht und angezeigt sind. Die hier gezeigten Subs sind zur Demonstration komplexer Strukturen programmiert. Für andere Zwecke müssen sie entsprechend abgeändert werden. |
sub analyze_array_pointer{
my($arg,$txt)=@_;
}my ($i,$v,$t); for($i=0;$i<@{$arg};$i++) {
$v = ${$arg}[$i];
}
$t = $txt.'['.$i.']'; analyze_pointer($v,$t); sub analyze_hash_pointer{
my($arg,$txt)=@_;
}
my($k,$v,$t); while(($k,$v)=each %$arg) {
$t = $txt."{'".$k."'}";
}
analyze_pointer($v,$t); |
|
Ausgabe-Beispiel
für das Skalar $x und den Pointer \$x
auf das gleiche Skalar.
|
x = 123
\x = pointer to SCALAR ${\x} = 123 |
|
Ausgabe-Beispiel
für das komplexe Array @ba Es enthält die beiden skalaren Elemente $ba[0] und $ba[2] sowie im Element $ba[1] ein weiteres Array mit 3 skalaren Elementen. |
ba = pointer to ARRAY
ba[0] = abc ba[1] = pointer to ARRAY ba[1] [0] = 10 ba[1] [1] = 11 ba[1] [2] = 12 ba[2] = xyz |
|
Ausgabe-Beispiel
für das komplexe Hash %hb Es enthält die skalaren Elemente $hb{'abc'} und $hb{'xyz'}, das komplexe Array $hb{'test'} und das Hash $hb{'rst'} Im Gegensatz zu Arrays ist die Reihenfolge der Elemente in einem Hash unbestimmt ! |
hb = pointer to HASH
hb{'test'} = pointer to ARRAY hb{'test'}[0] = abc hb{'test'}[1] = pointer to ARRAY hb{'test'}[1][0] = 10 hb{'test'}[1][1] = 11 hb{'test'}[1][2] = 12 hb{'test'}[2] = xyz hb{'abc'} = Eva hb{'xyz'} = Zap hb{'rst'} = pointer to HASH hb{'rst'}{'r'} = 123 hb{'rst'}{'g'} = 135 hb{'rst'}{'b'} = 246 |
Unterscheidung Pointer - SkalarKomplexe strukturen werden in Perl meist durch mehrdimensionale Hashes dargestellt. Arrays und Hashes können als Elemente nicht nur einfache Variable (Skalare, z.B. eine Zahl oder einen Text) sondern weitere Arrays oder Hashes enthalten. Bei der Verarbeitung der Elemente muss man unterscheiden, ob das untersuchte Element ein Skalar oder ein Array/Hash ist.Im Beispiel wird zunächst ein Test-Hash %hsh erzeugt: Das Hash erhält 3 Elemente: 2 der Elemente sind Skalare, ein Element ist selbst ein Hash aus 2 Elementen. Danach werden die Elemente von %hsh in einer Schleife verarbeitet. Mit Funktion ref wird zwischen Skalaren und Referenzen (Pointern) unterschieden. Die Funktion wird hier vereinfacht wie ein Operator verwendet. |
# Erzeugung eines mehrdimensionalen Test-Hash:
$hsh{'abc'}=123;$hsh{'xyz'}='Text'; %hsub=('vn'=>'Franz','zn'=>'Weber'); $hsh{'sub'}=\%hsub; # Verarbeitung der Elemente:
while(($k1,$v1) = each %hsh) {
if(ref $v1) {
}
print "Element $k1 (Pointer) = $v1\n";
}while(($k2,$v2) = each %$v1) { print "Sub-Element $k1,$k2 = $v2\n";
}
else{ print "Element $k1 (Skalar) = $v1\n";
}
|
Unterscheidung Array - HashDie Funktion ref gibt den Typ des Arguments zurück.• Wenn das Argument ein Skalar ist, dann wird ein leerer Text zurückgegeben. • Wenn das Argument eine Refenz (Pointer) ist, dann wird ein Text (String) zurückgegeben, welcher den Typ beschreibt. Die wichtigsten Rückgabe-Werte sind ARRAY und HASH |
Feststellung des Typs einer Referenz:
# Herstellung eines Pointers
$v = \@abc;
# $v = \%xyz;
# Analyse des Pointers
$test = ref($v)print "v ist ein Pointer -> $test\n";
# Ausgabe:
# v ist ein Pointer -> ARRAY |
Unterprogrammeerhalten in Perl ihre Argumente im Array _@Normalerweise werden die übergebenen Argumente gleich zu Beginn an eine anonyme Liste von lokalen Variablen übergeben. Wenn sich in dieser Liste ein lokales Array befindet, dann saugt es alle folgenden Elemente von @_ auf ! Das kann schwer auffindbare Fehler ergeben. Im Beispiel ist subtest1 sauber programmiert, subtest2 dagegen gefährlich. ● Die Liste der lokalen Variablen darf daher nur ein einziges Array oder Hash enthalten, und zwar nur als letztes Element ! ● Verwenden sie zur Übergabe von Arrays besser nur deren Adressen (nächstes Kapitel) ! |
subtest1('a','b','c',10,20,30);
subtest2('a','b','c',10,20,30); # . . .
sub subtest1 {
my($v1,$v2,$v3,@a1)=@_;
}
# $v1='a'; $v2='b'; $v3='c';
# @a1 = (10,20,30); sub subtest2 {
my(@a1,$v1,$v2,$v3)=@_;
}
# @a1 = ('a','b','c',10,20,30);
# Variable $v1 . . $v3 sind leer ! |
Adressen (references, pointer)Perl bietet die Möglichkeit nicht nur mit Variablen, sondern auch mit deren Adressen zu arbeiten.♦ Details zu Adressen von Arrays und Hashes |
Die Übergabe von Adressen an Stelle der Arrays bietet wichtige Vorteile: • Beliebig viele Arrays können an subs übergeben werden. • Die Reihenfolge der Argumente ist beliebig. |
Adressen von Arrays und Hashes |
|
Perl bietet die Möglichkeit nicht nur mit Variablen, sondern auch mit deren
Adressen zu arbeiten. Das Beispiel demonstriert die verwendete Syntax mit einem
Array.Variable → AdresseEin \ backslash-Zeichen vor dem Namen einer Variablen erzeugt die Adresse der Variablen - auch bei Arrays und Hashes.Adresse → VariableGeschwungene {} Klammern um eine skalare Variable bewirken ihre Interpretation als Adresse.Beispiel rechts: Hier wird die Adresse von Array $ara an die Variable $aradr übergeben. Danach wird demonstriert, wie man wahlweise den Variablen-Namen @ara oder die Adresse $aradr verwenden kann. Die Syntax-Variante mit dem -> Operator funktioniert ähnlich wie in der Programmiersprache C++. |
my($aradr,@ara,$v);
@ara = (3..6); $aradr = \@ara; # ist äquivalent zu
$aradr = [3,4,5,6];
# @ara ist äquivalent zu @{$aradr}
$v = $ara[2];# Auswahl von Elementen: $v = ${$aradr}[2]; $v = $aradr->[2]; # Array-Länge: $arlen = @{$aradr}; |
|
Verwendung von Adressen (references, pointer) mit Hashes: Das Beispiel funktioniert sinngemäß genauso wie bei den Arrays gezeigt. |
my($hadr,%ha,$v);
%ha = ('r'=>12,'g'=>23,'b'=>34); $hadr = \%ha; # ist äquivalent zu
$hadr = {'r'=>12,'g'=>23,'b'=>34};
# %ha ist nun äquivalent zu %{$hadr}
$v = $ha{'g'};# Auswahl von Elementen: $v = ${$hadr}{'g'}; $v = $hadr->{'g'}; |
Mehrdimensionale Arrays und HashesMit der gezeigten Adressen-Logik werden in Perl komplexe (mehrdimensionale) Strukturen aufgebaut:Ein Array oder Hash kann als Elemente nicht Daten sondern auch die Adressen weiterer Arrays (Pointer) enthalten. Derartige Sub-Arrays können selbst wieder Daten oder weitere Pointer enthalten. Das Beispiel zeigt den Aufbau eines einfachen 2dimensionalen Arrays, zuerst in einzelnen Schritten: Jede Zeile wird als eigenes Array angelegt, aus den Adressen der beiden Zeilen wird das 2dimensionale Array erstellt. Danach wird das gleiche Beispiel in verkürzter Syntax gezeigt. Zuletzt wird der Zugriff auf einzelne Elemente mehrdimensionaler Arrays demonstriert. Die detaillierte Variante zeigt, dass mit Index [0] die erste der beiden Adressen von Array @a2d ausgewählt wird. Danach wird (-> Operator) jenes Array aufgesucht, auf welches diese Adresse zeigt. Davon wird mit Index [1] das Element mit dem Wert 12 ausgewählt. Üblich ist die verkürzte Schreibweise, in welcher alle Pfeil -> Operatoren zwischen mehreren Indices wegfallen (letzte Zeile). |
# 2dim Array, 2 Zeilen mit je 3 Spalten
my(@z1,@z2,@a2d,$adrz1,$adrz2);
# Aufbau in Einzel-Schritten
@z1 = (11,12,13); # Zeile 1$adrz1 = \@z1; @z2 = (21,22,23); # Zeile 2 $adrz2 = \@z2; @a2d = ($adrz1,$adrz2); # ist äquivalen zu $adrz2=[21,22,23]; @a2d = ($adrz1,$adrz2); # ist äquivalen zu # Adressierung von Zeile 1, Spalte 2, Wert 12 $v = $a2d[0][1]; |
Adressen als Argumente von Unterprogrammen● Es wird davon abgeraten, Arrays oder Hashes als Argumente an Unterprogramme zu übergeben. Das kann zu schwer auffindbaren Fehlern führen.● Die Übergabe der Adressen (Referenzen, Pointer) funktioniert rasch und problemlos. ♦ Details zur Übergabe von Arrays und Hashes an Unterprogramme. |
my @ar = (98,76,54);
my @br = ('z','y','x'); # Empfehlenswert:
mysub(\@ar1,$x,\@ar2);
# Nicht empfehlenswert !
# mysub(@ar,$x,@br); |
Arrays und Unterprogramme (subs) |
|
Arrays in main sind global● Alle Variablen, die im "Hauptprogramm" (main) deklariert wurden, sind global. Sie stehen in allen subs zum Lesen und Schreiben zur Verfügung.● Das gilt nur für main - Wenn ein Array innerhalb eines sub deklariert wurde, dann ist es nicht global ! Im Beispiel wird ein Array @ar in main deklariert und mit ganzen Zahlen 0..10 gefüllt. Im Unterprogramm s1 wird das Element $ar[1] geändert. Die Änderung wirkt sich global aus: Die beiden print-Befehle in main ergeben daher zuerst die Zahl 1, danach 111. Das Array wird weder von main an das Sub übergeben, noch vom Sub zurückgegeben ! |
my(@ar)=(0..10);
Ausgabe: 1,1,111
print $ar[1].','; s1(); print $ar[1]; sub s1 {
print $ar[1].',';
}
$ar[1]=111; |
|
Array als Argument übergeben
Man kann Arrays und Hashes als Argumente an ein
Sub übergeben.● In diesem Fall wird eine komplette Kopie des Arrays erzeugt und übergeben. ● Nur ein einziges Array oder Hash darf als Argument übergeben werden, und zwar nur als letztes Argument der Liste. - Andernfalls zieht das Array bei der Verarbeitung im Sub alle nachfolgenden Argumente an sich. ● Im sub kann man alle Daten der Kopie lesen und schreiben, Änderungen wirken zwar lokal (innerhalb des subs) jedoch nicht im Original-Array des aufrufenden Programms. ● An Stelle dieser Variante wird empfohlen, besser die Adressen als Argumente zu übergeben (nächster Absatz). |
my(@ar)=(0..10);
Ausgabe: 2,2,2
print $ar[2].','; s2(@ar); print $ar[2]; sub s2 {
my(@sar)=@_;
}
print $sar[2].','; $sar[2]=222; |
Array-Adresse als Argument übergeben● Man kann Adressen von Arrays und Hashes an ein sub übergeben. In diesem Fall hat das Sub vollen Zugriff auf das Original-Array.● Änderungen wirken global, d.h. sowohl im lokalen sub als auch im Array des aufrufenden Programms. Das gilt auch dann, wenn das aufrufende Programm selbst ein Sub ist. ● (Nur) Auf diese Weise kann man beliebig viele Arrays an ein sub übergeben, und zwar in beliebiger Reihenfolge - Es werden lediglich skalare Variable übergeben, manche davon mit einer speziellen Bedeutung als Adressen. ♦ Details zur Verwendung von Adressen |
my(@ar)=(0..10);
Ausgabe: 3,3,333
print $ar[3].','; s3(\@ar); print $ar[3]; sub s3 {
my($sar)=@_;
}
# Länge:
my $alen=@{$sar};
# Element:
print ${$sar}[3].',';
# Änderung:
${$sar}[3]=333; |
Hash-Adresse als Argument übergeben:Die gleiche Methode kann auch zur Übergabe der Adressen von Hashes verwendet werden: Das Programm print_hash gibt eine Liste aller Hash-Elemente für jedes beliebige Hash aus. Die Elemente sind zur Vereinfachung nicht sortiert.● Wenn $sh die Adresse eines Hash %h ist, dann + wird das gesamte Hash statt mit %h mit %{$sh} angesprochen. + wird ein Element statt mit $h{'k'} mit ${$sh}{'k'} adressiert. Das Sub hat vollen Zugriff auf das Original Hash, kann daher auch alle Daten ändern. |
my %hcol;
$hcol{'r'} = 123; $hcol{'g'} = 234; $hcol{'b'} = 34; print_hash(\%hcol); sub print_hash {
my($sh) = @_;
}
my($k,$v,$i)=0; while (($k,$v)=each %{$sh}) { print '('.$i++.") $k = $v\n";
}
|
Array aus einem sub zurückgeben● Wenn ein Array oder Hash in einem Sub erzeugt wird, dann wird es einfach mit einer return-Anweisung an das aufrufende Programm übergeben.● Für wenige Elemente ist es oft bequemer, Listen an Stelle von Arrays zu verwenden. ● Bei der Rückgabe eines Array oder Hash werden allenfalls bereits vorhandene Elemente gelöscht, auch dann wenn das neue Array weniger Elemente enthält als das alte. Das Sub s4 erzeugt ein Array und gibt es zurück - wahlweise an ein Array oder an eine Liste von Variablen. Das Sub s5 erzeugt eine Liste, die im Beispiel an ein Array übergeben wird. |
my (@br)=s4();
my ($x,$y,$z)=s4(); my (@br)=s5(); sub s4 {
my(@sbr)=(41..43);
}return @sbr; sub s5 {
my $f=55;
}
return (54,$f,56); |
Rückgabe mehrerer Arrays oder HashesDas Beispiel rechts funktioniert zwar, gibt jedoch nur ein einziges Array zurück.Die return-Anweisung des Sub fügt mehrere Argumente in der angegebenen Reihenfolge zu einem einzigen Array, welches zurückgegeben wird. Die zurückgegebenen Daten verhalten sich daher wie ein einziges Array mit 6 Elementen. Das aufrufende (Haupt)-Programm kann die Rückgabe-Daten unterschiedlich verarbeiten: Wenn die Daten an skalare Variable zugewiesen werden, dann erhält jede ein einzelnes Array-Element. Wenn die Daten an ein Array zugewiesen werden, dann erhält dieses alle noch verbleibenden Daten. Im Beispiel rechts erhält daher das erste Array @a1 alle zurückgegebenen Daten, das Array @a2 bleibt leer. |
Funktioniert nicht wie erwartet:
my(@a1,@a2) = rma();
print "a1 = @a1 \n"; print "a2 = @a2 \n"; sub rma{
my @ara=(1..4);
}
my @arb=(101,102); return @ara,@arb;
# Ausgabe:
# a1 = 1 2 3 4 101 102 # a2 = |
|
Mehrere Arrays oder Hashes lassen sich unterscheiden, wenn ein Sub die
Adressen der Arrays als Pointer zurückgibt: Diese
Variante gibt die gleichen Daten zurück, sie lassen sich jedoch wie
gewünscht 2 verschiedenen Arrays zuordnen. Das Sub gibt Pointer auf Arrays zurück. Diese sind Skalare, daher enthalten die Rückgabe-Daten nur 2 Elemente. Das aufrufende (Haupt)-Programm übergibt die erhaltenen Daten an 2 skalare Variable (=Pointer). Mit Hilfe dieser Pointer werden Daten an die beiden Arrays @a1 und @a2 übergeben. Diese Variante funktioniert wie erwartet, arbeitet jedoch mit großen Arrays langsam und aufwändig, da die Daten zwischen den beiden unabhängigen Arrays @a1 und @{$pa1} kopiert werden. |
Funktioniert wie erwartet:
my($pa1,$pa2) = rma();
my @a1=@{$pa1}; print "a1 = @a1 \n"; my @a2=@{$pa2}; print "a2 = @a2 \n"; sub rma{
my @ara=(1..4);
}
my @arb=(101,102); return \@ara,\@arb;
# Ausgabe:
# a1 = 1 2 3 4 # a2 = 101 102 |
|
Noch besser verzichtet man überhaupt auf die Rückgabe mehrerer Arrays.
Man deklariert die Arrays bereits im aufrufenden (Haupt)-Programm und übergibt
deren Adressen an das Sub. Das Sub benötigt keine eigenen lokalen Arrays, sondern verwendet die als Argument erhaltenen Pointer. Das Sub greift damit direkt auf die Elemente der entsprechenden Arrays zu. Daher braucht es auch keine Daten zurückzugeben. Das aufrufende (Haupt)-Programm benötigt keine Anweisungen zur Annahme zurückgegebener Daten. Es kann ohne weitere Maßnahmen mit den Arrays weiter arbeiten. |
rma(\my @a1,\my @a2);
print "a1 = @a1 \n"; print "a2 = @a2 \n"; sub rma{
my($p1,$p2)=@_;
}
@{$p1}=(1..4); @{$p2}=(101,102);
# Ausgabe:
# a1 = 1 2 3 4 # a2 = 101 102 |
Arrays in Unterprogrammen |
|
Array @_Alle Argumente werden im Array @_ an ein Sub übergeben. Wer Perl nicht kennt, muss sich an derart ungewöhnliche Namen erst einmal gewöhnen.Die Argument-Übergabe in einem Array hat zunächst einmal den Vorteil, dass die Anzahl der Argumente dynamisch ist und keine lästigen Fehler durch unpassende Anzahl von Argumenten auftreten: Jedes sub kann selbst bestimmen, wieviele Argumente übergeben wurden, und darauf reagieren. Meistens werden z.B. vorhandene Argumente übernommen, fehlende durch Standard-Werte ersetzt. |
$s = my_add(11,22);
sub my_add {
my($x,$y);
}
$x = $_[0]; $y = $_[1]; return $x+$y; |
|
Diese Version ist bereits eleganter: Die ersten beiden Elemente des
Argumente-Arrays @_ werden an die beiden lokalen
Variablen $x und $y überwiesen.
Die () auf der linken Seite definieren eine Liste mit den Elementen $x und $y. Ohne Argumente wird 0 zurückgegeben, bei 1 Argument wird dieses zurückgegeben, mit 2 Argumenten wird die Summe korrekt berechnet, alle weiteren Argumente werden ignoriert. |
sub my_add {
my($x,$y)=@_;
}
return $x+$y; |
|
Diese Version zeigt, wie das sub dynamisch programmiert
werden kann: Jede beliebige Anzahl von Zahlen-Argumenten wird addiert und
zurückgegeben. Das Programm bestimmt die Anzahl der Argumente (Array-Eigenschaften) und entnimmt dem Array so viele Argumente $_[$i] wie tatsächlich übergeben wurden. Das sub lässt sich noch knapper formulieren, aber dann ist es nur mehr für Perl-Gurus lesbar. |
sub my_add {
my $nargs = $#_+1;
}
my ($s,$i)=0; for($i=0;$i<$nargs;$i++) { $s+=$_[$i];
}return $s; |
|