Woche 1

# Tag 4

#
Mit Listen und Arrays arbeiten

Am Tag 2 und 3 haben wir uns in erster Linie mit Skalaren, also einzeln vorliegenden Daten, befaßt. Heute reden wir über Gruppen von Objekten, nämlich Listen und Arrays, und was Sie damit anstellen können. Sie lernen heute unter anderem,

- was Arrays und Listen sind, und in was für Variablen man sie speichert,
- wie man Arrays erstellt und gebraucht,
- was Listen- und Skalarkontext (für Sie und Perl) bedeutet,
- mehr über `<STDIN>` und wie Sie Daten in Listen einfügen und
- wie Sie Listen ausgeben.

## Listendaten und -variablen

Wenn man skalare Daten als einzelne Objekte definiert, kann man sich Listendaten als eine Sammlung mehrerer Objekte - genauer gesagt als Satz von Skalaren - vorstellen. Genau wie der Begriff Skalar sowohl Zahlen als auch Strings umfaßt, bezieht sich der Begriff Liste üblicherweise auf zwei spezielle Konstrukte: Arrays und Hashes. Wir werden heute über Arrays reden und morgen tiefer in die Hashes einsteigen.

Ein Array ist eine Sammlung von beliebig vielen Skalaren. Auf jedes einzelne Element kann zugegriffen werden, indem man sich auf die Nummer seiner Position im Array (seinen Index) bezieht. Array-Indizes beginnen in Perl bei 0. Zur Illustration zeigt Abbildung 4.1 ein einfaches Array mit ein paar Zahlen und Strings darin.

Abbildung 4.1: Anatomie eines Arrays

Arrays sind geordnet. Das bedeutet, sie haben ein erstes Element, ein letztes, und alle Elemente dazwischen stehen in einer bestimmten Reihenfolge. Man kann die Reihenfolge der Elemente durch Sortierung ändern oder die Elemente durchlaufen, eins nach dem andern, vom Anfang bis zum Ende.

Arrays werden in Arrayvariablen gespeichert, wie Skalare in Skalarvariablen gespeichert werden. Eine Arrayvariable beginnt mit dem at-Zeichen `@`. Abgesehen von diesem ersten Zeichen gelten für Namen von Arrayvariablen die gleichen Regeln wie für Skalarvariablen, nämlich die allgemeinen Variablennamensregeln:

- Variablennamen können insgesamt bis zu 256 Zeichen lang sein und Zahlen, Buchstaben und Unterstriche enthalten.
- Namen sind case-sensitiv. Das heißt, es wird zwischen Groß- und Kleinbuchstaben unterschieden.
- Arrayvariablen können, anders als Skalarvariablen, mit einer Zahl anfangen, dürfen dann aber auch weiterhin nur noch andere Zahlen enthalten.

Die Namen von Skalar- und Arrayvariablen können nicht miteinander in Konflikt geraten. Die Skalarvariable `$x` ist eine andere Variable als die Arrayvariable `@x`.

Listendaten haben auch eine Rohform, die man (Überraschung!) eine Liste nennt. Eine Liste ist einfach eine Folge von Elementen. Sie können eine Liste einer Variablen zuweisen, eine Liste durchlaufen, um jedes ihrer Elemente auszugeben, sie in andere Listen verschachteln oder als Funktionsargument benutzen. Normalerweise werden Sie Listen zum Erstellen von Arrays und Hashes verwenden oder um Daten von einer Liste an eine oder mehrere andere zu übergeben. Arrays sind einfach eine bestimmte Form von Liste, aber im wesentlichen das gleiche, ja austauschbar.

## Definition und Gebrauch von Listen und Arrays

Was macht man mit einem Perl-Array? Man legt es an (definiert es), weist es einer Arrayvariablen zu, packt Elemente hinein und liest oder nimmt sie wieder heraus, oder man zählt, wie lang das Array ist, das heißt wie viele Elemente es hat. Man kann noch eine ganze Menge mehr mit Arrays und ihren Daten anstellen, aber fangen wir mit den Grundlagen an.

Zur Definition von Perl-Arrays gibt es gar nicht viel zu sagen. Im Unterschied zu anderen Sprachen, in denen Arrays sorgfältig eingerichtet werden müssen, bevor man sie verwenden kann, erscheinen Arrays in Perl wie von Geisterhand dort, wo Sie sie brauchen, und sie wachsen und schrumpfen jederzeit und ganz von allein auf die richtige (also der aktuellen Anzahl der Elemente entsprechenden) Größe. In Perl- Arrays können Sie außerdem nicht nur jede Art von skalaren Daten speichern - Zahlen, alphanumerische Zeichen oder eine Mischung aus beiden -, sondern auch beliebig viele. Wie viele Elemente Sie in ein Array packen, bestimmen allein Sie und der Ihnen verfügbare Speicher.

> Arrays können auch Referenzen enthalten, aber über die haben Sie noch nichts gelernt. Haben Sie Geduld, am Tag 19 wird alles klar.

### Listen erstellen

Wie ich bereits erwähnt habe, bezieht sich der Begriff Liste auf einen unspezialisierten Satz von Daten, ein Array kann als eine Liste betrachtet werden, die in einer Arrayvariablen gespeichert ist. Sie können Listen überall verwenden, wo Arrays erwartet werden und umgekehrt.

Eine Liste erstellen Sie mit der Listensyntax: Tippen Sie die Elemente ein, setzen Sie Kommata dazwischen und alles zusammen in Klammern. Sie haben dann eine rohe Liste, mit der Sie ein Array oder einen Hash erstellen können, je nachdem, was für einer Variablen sie die Liste zuweisen. Betrachten wir jetzt ein paar Array-Beispiele. Hashes behandeln wir morgen.

Im folgenden Beispiel erstellen wir ein Array namens `@zahlen`, das vier Elemente hat:

    @zahlen = (1, 2, 3, 4);

Listen aus Strings sind genauso einfach:

    @strings = ('Petersilie', 'Salbei', 'Rosmarin', 'Thymian');

Die Listensyntax kann jede beliebige Mixtur aus Strings, Zahlen, Skalarvariablen, Ausdrücken, die Skalare liefern, und anderen Elementen enthalten:

    @kram = ('garbonzo', 3.145, $zaehler, 'Schokolade', 4 / 7, $a++);

Für eine leere Liste schreiben Sie einfach nichts zwischen die Klammern (nicht einmal ein Leerzeichen):

    @nichts = ();

Sie können Listen ineinander verschachteln, aber diese Unterlisten werden im endgültigen Array nicht festgehalten. Alle Elemente werden in ein einziges Array aufgedröselt und leere Unterlisten dabei entfernt:

    @kombination = (1, 4, (6, 7, 8), (), (5, 8, 3));
    # ergibt (1, 4, 6, 7, 8, 5, 8, 3);

Ähnlich verhält sich Perl beim Verschachteln von Arrayvariablen. Alle Elemente in den Unterarrays werden zu einer einzigen größeren Liste verkettet:

    @kombination2= (@zahlen, @strings);
    # ergibt (1, 2, 3, 4, 'Petersilie', 'Salbei', 'Rosmarin', 'Thymian')
    @zahlen = (@zahlen, 5);     # ergibt  (1, 2, 3, 4, 5);

Sagen wir, Sie definieren ein Array aus Ein-Wort-Strings, zum Beispiel Wochentagen oder eine Namensliste. Ein sehr gebräuchlicher Perl-Trick zum Erstellen von Ein-Wort- Arrays ist der Einsatz der speziellen `qw`-Syntax (von »quote word«, »zitiere Wort«). Die `qw`-Syntax erspart Ihnen das Eintippen der Anführungszeichen und Kommata und macht das Array um einiges lesbarer:

    @monate = qw(
       Januar Februar Maerz
       April Mai Juni
       Juli August September
       Oktober November Dezember
    );

### Listen mit dem Bereichsoperator erstellen

Sie brauchen eine Liste aller Zahlen zwischen 1 und 1000? Hierfür alle Zahlen einzeln einzutippen, wäre schändliche Zeitverschwendung. Irgendeine Schleife, die eine Zahl nach der anderen hinzufügt, wäre da schon einfacher, aber immer noch ziemlich kludgy. (Ein kludge ist alter Freak-Jargon für eine nicht so optimale Lösung eines Problems. Einige irregeführte Einzelpersonen würden argumentieren, dass Perl selbst ein kludge ist. Doch das ignorieren wir mit einem Lächeln.)

In welcher Situation auch immer Sie denken: »Es muss einen einfachen Weg geben, das hinzukriegen«, stehen die Chancen wirklich gut, dass es einen solchen einfachen Weg in Perl auch gibt. In diesem Fall ist es der Bereichsoperator (`..`), auch Range- Operator genannt. Um ein Array mit den Zahlen von 1 bis 1000 zu erstellen, schreiben Sie einfach:

    @vieleZahlen = (1 .. 1000);

Das war's, Sie sind fertig. Das Array `@vieleZahlen` enthält jetzt 1000 Elemente, von 1 bis 1000. Der Range-Operator arbeitet so, dass er vom linken Operanden jeweils um 1 bis zum rechten Operanden hochzählt (runterzählen kann man damit nicht).

Der Bereichsoperator funktioniert auch mit Buchstaben:

    @alphabet = ('a' .. 'z');   # enthält 26 Elemente

Wir werden am Tag 6 noch einmal auf den Bereichsoperator zurückkommen, wenn wir uns mit Iterationen befassen.

### Zuweisung und Listen

Bis jetzt haben wir die Listensyntax auf der rechten Seite des Zuweisungsausdrucks verwendet, um eine Liste zu erstellen und sie einer Arrayvariablen zuzuweisen. Sie können auf die linke Seite einer Zuweisung aber auch eine Liste von Variablenreferenzen stellen. Perl wird diesen Variablen dann auf Grundlage der Liste rechts Werte zuweisen.

Nehmen Sie zum Beispiel folgenden Ausdruck:

    ($a, $b) = (1, 2);

Auf beiden Seiten der Zuweisung haben wir hier eine Liste. Doch nach wie vor ist links die Seite, auf der Variablen gespeichert werden. Perl weist die Werte der rechten Liste den Variablen in der linken Liste zu, und zwar in der Reihenfolge, in der sie auftreten. So erhält `$a` den Wert `1` und `$b` den Wert `2`. Das ist natürlich genau das gleiche, als würden Sie die Werte in getrennte Zeilen schreiben, aber auf diese Art können Sie bequemer mehreren Variablen Werte zuweisen. Beachten Sie, dass die Zuweisungen parallel ablaufen, so dass man auch schreiben kann:

    ($x, $y) = ($y, $x);

Dieses Beispiel vertauscht die Werte von `$x` und `$y`. Nichts passiert vor dem anderen, deswegen wird es wie gewünscht funktionieren.

Wenn auf beiden Seiten eines Zuweisungsoperators Listen stehen, gilt die Regel, dass jede Variable auf der linken Seite einen Wert aus der rechten Seiten erhält. Wenn es links mehr Variablen als rechts Werte gibt, dann erhalten die übrigen Variablen den undefinierten Wert. Gibt es mehr Werte als Variablen, werden die übrigbleibenden Werte ignoriert.

Sie können auf die rechte Seite auch Arrayvariablen stellen. Das Array wird dann in seine Elemente zerlegt, und die Werte werden wie im Falle einer normalen Listensyntax zugewiesen:

    ($a, $b) = @zahlen;

Sie können Arrays sogar auf beiden Seiten in Listen verschachteln - sie werden in ihre Elemente aufgedröselt und nach den obigen Regeln zugewiesen - mit einer Ausnahme:

    ($a, @mehr) = (10, 11, 12, 13, 14);

In diesem Beispiel erhält `$a` den Wert `10` und `@mehr` erhält die Liste (`11, 12, 13, 14`). Arrayvariablen auf der linken Seite einer Listenzuweisung sind gefräßig - das heißt, sie speichern alle noch folgenden Elemente der Liste auf der rechten Seite der Zuweisung. Das ist wichtig, wenn Sie zum Beispiel folgendes Beispiel betrachten:

    ($a, @mehr, $b) = (10, 11, 12, 13, 14);

In diesem Fall erhält `$a` den Wert `10`, @mehr die Liste (`11, 12, 13, 14`) und `$b` den undefinierten Wert. Weil das Array alle verbleibenden Werte auf der rechten Seite »auffrißt«, bleibt keiner mehr übrig, der `$b` zugewiesen werden könnte.

### Auf Array-Elemente zugreifen

Nun haben Sie also ein Array, vielleicht mit ein paar in Listenschreibweise vorgegebenen Elementen, oder einfach ein leeres Array, das Sie später mit Werten (beispielsweise Benutzereingaben) füllen möchten.

Um auf ein Array-Element zuzugreifen, verwenden Sie die `[]`-Syntax, die Sie vielleicht aus anderen Sprachen kennen: Der Index des Elements, auf das Sie zugreifen wollen, folgt in eckigen Klammern auf den Arraynamen:

    $zahlen[4];

Dieses Beispiel liefert Ihnen den Wert des fünften Elements im Array `@zahlen`. (Sie erinnern sich, der Index startet bei 0.) Das folgende Beispiel ändert den Wert des ersten Elements in `10`:

    $zahlen[0] = 10;

Halten Sie kurz an, und betrachten Sie diese Syntax etwas genauer. Kommt sie Ihnen komisch vor? Sie sollte. Wir beziehen uns hier auf das fünfte Element in einem Array namens `@zahlen`, aber wir benutzen etwas, das wie eine Skalarvariable aussieht. Das ist kein Druckfehler - so arbeitet die Syntax. Sie verwenden `@arrayname,` um sich auf das ganze Array zu beziehen, und `$arrayname[index]`, um auf ein einzelnes Element innerhalb dieses Arrays zuzugreifen.

Das bedeutet nicht, dass Sie `$arrayname` nicht verwenden können, um auf einen normalen Skalar zu verweisen. `$arrayname`, `$arrayname[index]` und `@arrayname` sind ganz verschiedene Dinge. Am besten merkt man sich das, indem man sich daran erinnert, dass die Elemente von Arrays ja Skalare sind und man deswegen eine Skalarvariable braucht, um auf sie zuzugreifen, auch wenn sie innerhalb eines Arrays stehen (die Perl-Warnungen geben Ihnen Bescheid, falls Sie das vergessen).

Array-Indizes beginnen bei 0 wie in anderen Sprachen auch, und ein Index kann nur eine ganze Zahl (ein Integer) sein. So bezieht sich `$arrayname[0]` auf das erste Element in einem Array, `$arrayname[1]` auf das zweite und so weiter. Sie müssen nicht explizit eine Zahl als Index verwenden, Sie können auch eine Variable oder jeden anderen Ausdruck dafür nehmen, der zu einer Zahl ausgewertet wird:

    $array[$zaehler];    # das Element an Position $zaehler;

Was passiert, wenn Sie versuchen, auf ein Element zuzugreifen, das gar nicht existiert - zum Beispiel auf Position 5 in einem dreielementigen Array? Wenn Sie die Warnungen aktiviert haben, erhalten Sie eine Fehlermeldung. Anderenfalls erhalten Sie einen undefinierten Wert (`0` oder `""`, je nach Kontext).

### Wachsende Arrays

Wenn Sie hinter dem letzten Element eines Arrays ein weiteres Element anfügen, wird Perl das Array entsprechend vergrößern, das Element hinzufügen und eventuell dazwischenliegenden Elementen den undefinierten Wert zuweisen. Zum Beispiel:

    @t = (1, 2, 3);
    $t[5] = 10;

Das Array `@t` enthält nach diesen beiden Zeilen die Elemente `(1, 2, 3, undefiniert, undefiniert, 10)`. Wenn Sie versuchen, auf die undefinierten Werte in der Mitte zuzugreifen, wird Perl Sie warnen (vorausgesetzt, Sie haben die Warnungen angeschaltet).

Möchten Sie sichergehen, dass Sie es nur mit definierten Werten zu tun haben, können Sie Ihre Werte mit der Funktion `defined` überprüfen:

    if (!defined $array[$index]) {
       print "Element $index ist undefiniert.\n";
    }

Alternativ dazu können Sie ein Array-Element »leeren«, indem Sie es mit der Funktion `undef` auf den undefinierten Wert setzen:

    if (defined $array[$index]) {
       undef($array[$index]);
    }

Beachten Sie, dass das Array-Element, das Sie mit `undef` undefiniert gemacht haben, sich nach wie vor im Array befindet, nur dass es jetzt den undefinierten Wert hat. Um wirklich ein Element aus einem Array zu entfernen, müssen Sie es explizit löschen, indem Sie das Array neu konstruieren.

Die `undef`-Funktion kann übrigens überall eingesetzt werden, nicht nur in Arrays, um einer beliebigen Variablen den undefinierten Wert zuzuweisen. Man kann sie auch ohne jegliches Argument verwenden, dann gibt sie einfach den undefinierten Wert zurück, zum Beispiel:

    @loch_in_der_mitte = (1, undef, undef, undef, 5);

Weil man auf diese Art so leicht einen undefinierten Wert erhält, bezeichnet man ihn üblicherweise einfach mit `undef` (und für den Rest des Buches werde ich genau das tun).

### Das Ende eines Arrays finden

Weil Arrays in Perl jede beliebige Größe haben können, brauchen Sie eine Methode, das Ende eines Arrays zu finden. Schließlich muss zum Beispiel eine Schleife, die die Inhalte des Arrays durchläuft, wissen, wann Ende ist. Bequemerweise hat Perl hierfür eine eigene Syntax, die Ihnen die Indexnummer des letzten Array-Elements liefert: `$#array`. Mit dem Index des letzten Elements könnten Sie eine einfache Schleife bauen, die vom Index 0 bis zum letzten läuft. So könnten Sie zum Beispiel mit einem Konstrukt wie dem folgenden alle Elemente eines Arrays zeilenweise ausgeben:

    $i = 0;
    while ($i <= $#array) {
       print $array[$i++], "\n";
    }

> Für Operationen dieser Art verwendet man üblicherweise eine `for`- oder `foreach`-Schleife statt einer `while`-Schleife. Aber da Sie bis jetzt nur `while`-Schleifen gesehen haben, wollte ich hier keine andere nehmen. Wir werden uns `foreach` später in dieser Lektion noch ansehen.

Wenn Sie den Wert von `$#array` verändern, dann vergrößern bzw. verkleinern Sie das Array. Setzen Sie `$#array` auf einen größeren als den aktuellen Wert, erhalten alle neu hinzugekommenen Elemente den undefinierten Wert. Wenn Sie den Wert von `$#array` verkleinern, werden alle Elemente am Ende des Arrays verworfen.

Beachten Sie, dass man mit der Syntax `$#array` nicht die Länge des Arrays (oder die Anzahl der enthaltenen Werte) herausfindet. Weil Array-Indizes bei 0 beginnen, liefert Ihnen `$#array` einen Wert, der um eins kleiner ist als die Anzahl von Elementen im Array. Wie man die Länge eines Arrays ermittelt, ist Thema des nächsten Abschnitts.

### Die Länge eines Arrays herausfinden

Um die Anzahl der Elemente in einem Array zu ermitteln, verwenden Sie folgende Anweisung:

    $anzahl_elemente = @array;

Denken Sie darüber jetzt nicht lange nach, merken Sie sich einfach: Um die Anzahl der Elemente in einem Array zu erhalten, verwendet man eine Skalarvariable und weist ihr eine Arrayvariable zu. Ich werde später erklären, warum das funktioniert (im Abschnitt »Listen- und Skalarkontexte«).

### Listen und Arrays sortieren

In anderen Sprachen müssen Sie, wenn Sie die Inhalte eines Arrays sortieren möchten, vielleicht eine eigene Sortierroutine schreiben. Nicht so in Perl, da ist schon eine eingebaut. Zum Sortieren eines Arrays brauchen Sie lediglich die Funktion `sort`:

    @geordnete_zahlen = sort @zahlen;

Diese Zuweisung sortiert das Array `@zahlen` und weist die neue Liste dann der Variablen `@geordnete_zahlen` zu. Das @zahlen-Array bleibt unverändert, also unsortiert.

Diese besonders simple `sort`-Variante sortiert die Inhalte von `@zahlen` in ASCII- Reihenfolge - das heißt, dass `5543` im Array vor `94` steht (weil 5 vor 9 kommt). Um das Array in numerischer Reihenfolge zu sortieren, setzen Sie nach `sort` einen speziellen Vergleichsausdruck:

    @geordnete_zahlen = sort { $a <=> $b } @zahlen;

Der Teil in den geschweiften Klammern legt fest, wie `sort` das Array mit Hilfe des Vergleichsoperators `<=>` sortiert. Wir werden an Tag 8 zum Anpassen von `sort`- Routinen kommen. Jetzt können Sie sich erst einmal folgendes merken: Um ein Array nach Zahlen zu sortieren, verwenden Sie `sort` mit einem Vergleich. Um ein Array alphabetisch zu sortieren, nehmen Sie die Kurzform ohne den Vergleich.

### Alle Elemente eines Arrays durchlaufen

Vorhin habe ich Ihnen in einem Beispiel eine `while`-Schleife gezeigt, die alle Elemente eines Arrays durchläuft, vom ersten Element zum letzten. Viel gebräuchlicher für eine solche Iteration über ein Array, wie ein solcher Durchlauf auch genannt wird, ist allerdings die Verwendung einer `foreach`-Schleife wie hier:

    foreach $x (@liste) {
       # mache etwas mit dem jeweiligen Element
    }

Die `foreach`-Schleife wird für jedes Element der Liste innerhalb der Klammern einmal ausgeführt (hier ist es das Array `@liste`, aber es könnte auch eine rohe Liste, ein Bereich oder alles andere sein, was Ihnen als Ergebnis eine Liste liefert (zum Beispiel die `sort`-Funktion.) Bei jedem Listenelement wird der Wert dieses Elements einer Skalarvariablen (hier `$x`) zugewiesen und der Code zwischen den geschweiften Klammern ausgeführt. Sie könnten zum Beispiel eine `foreach`-Schleife verwenden, um jedes Listenelement auszugeben, sie alle zusammenzufügen oder sie auf den Wert `undef` zu überprüfen.

Mehr über `foreach` erfahren Sie am Tag 6 und morgen, wenn wir über Hashes reden.

## Ein Beispiel: Mehr Statistik

Erinnern Sie sich an das simple Statistikskript von gestern? Man sollte nacheinander Zahlen eingeben, das Skript berechnete dann Anzahl, Summe und Durchschnitt. Lassen Sie uns dieses Skript heute etwas verändern, so dass es die eingegebenen Zahlen in ein Array speichert. Haben wir nämlich die Zahlen auch nach der Eingabe noch parat, können wir mehr mit ihnen anstellen - sie zum Beispiel sortieren oder den Median herausfinden (ein Zahlenwert, der die Zahlenliste so halbiert, dass die Hälfte der Zahlen kleiner und die andere Hälfte größer als dieser Wert ist).

Die neue Version des Skripts könnte zum Beispiel so ablaufen:

    % mehrstats.pl
    Bitte eine Zahl eingeben: 4
    Bitte eine Zahl eingeben: 5
    Bitte eine Zahl eingeben: 3

(Und so weiter, aus Platzgründen hier nicht aufgeführt.)

    Bitte eine Zahl eingeben: 47
    Bitte eine Zahl eingeben: 548
    Bitte eine Zahl eingeben: 54
    Bitte eine Zahl eingeben: 5485
    Bitte eine Zahl eingeben:

    Anzahl der eingegebenen Zahlen: 49
    Summe der eingegebenen Zahlen: 10430
    Hoechste Zahl: 5485
    Niedrigste Zahl: 2
    Durchschnitt: 212.86
    Median: 45
    %

Schon auf den ersten Blick erkennt man, worin sich dieses Statistikskript von der gestrigen Version unterscheidet:

- Es errechnet die höchste und die niedrigste der eingegebenen Zahlen.
- Es ermittelt den Median (der Wert in der Mitte einer sortierten Liste aller Zahlen).

Im Code finden wir einen weiteren bedeutenden Unterschied zwischen dieser und der letzten Version: Wir speichern die Eingabedaten diesmal in ein Array, anstatt sie einfach zu verwerfen. (Der Eingabevorgang hat sich nicht verändert: Wenn Sie das Skript ausführen, beenden Sie die Dateneingabe nach wie vor mit einer leeren Zeile). Listing 4.1 zeigt den Code für das neue Skript.

Listing 4.1: Das Skript mehrstats.pl

    1:  #!/usr/bin/perl -w
    2:
    3:  $input = '';  # Benutzereingabe
    4:  @nums = ();   # Zahlen-Array
    5:  $count = 0;   # Zaehler
    6:  $sum = 0;     # Summe
    7:  $avg = 0;     # Durchschnitt
    8:  $med = 0;     # Median
    9:
    10: while () {
    11:   print 'Bitte eine Zahl eingeben: ';
    12:   chomp ($input = <STDIN>);
    13:   if ($input ne '') {
    14:     $nums[$count] = $input;
    15:     $count++;
    16:     $sum += $input;
    17:    }
    18:   else { last; }
    19: }
    20:
    21: @nums = sort { $a <=> $b } @nums;
    22: $avg = $sum / $count;
    23: $med = $nums[$count /2];
    24:
    25: print "\nAnzahl der eingegebenen Zahlen: $count\n";
    26: print "Summe der eingegebenen Zahlen: $sum\n";
    27: print "Hoechste Zahl: $nums[$#nums]\n";
    28: print "Niedrigste Zahl: $nums[0]\n";
    29: printf("Durchschnitt: %.2f\n", $avg);
    30: print "Median: $med\n";

Diese Version des Statistikskripts besteht aus vier Abschnitten: Initialisierung, Dateneingabe, Sortierung und Statistikberechnung und schließlich Ausgabe der Ergebnisse.

Der Initialisierungsabschnitt, Zeile 3 bis 7, ist derselbe wie im vorherigen Skript, außer dass wir zwei Variablen hinzugefügt haben: eine Arrayvariable (`@nums`) in Zeile 4 zum Speichern der numerischen Eingaben und die `$med`-Variable in Zeile 8 für den Medianwert. Wie bei anderen Variablen bräuchten wir `@nums` eigentlich nicht initialisieren, aber es sieht nett aus und verschafft uns am Anfang des Skripts einen Überblick über alle verwendeten Variablen.

Die Zeilen 10 bis 19 sind die neue `while`-Schleife für die Dateneingabe. Wenn Sie diese Version mit der von gestern vergleichen, werden Sie bemerken, dass hier eigentlich nicht viel Neues steht. Wir lesen immer noch eine Zahl pro Zeile ein, inkrementieren den Zähler `$count` und aktualisieren mit jeder Zahl die Variable `$sum` (und wir überprüfen auch immer noch nicht, ob die Eingaben wirklich nur Zahlen enthalten). Der einzige Unterschied ist die Zeile 14, in der wir bei jedem Schleifendurchlauf die neue Zahl dem Array `@nums` hinzufügen. Die `$count`-Variable erfüllt hier einen doppelten Zweck: Sie ist nicht nur Zähler der Eingaben, sondern auch Array-Index (beachten Sie, dass wir das Array korrekt bei `0` starten, weil wir `$count` erst danach, in Zeile 15, erhöhen.

Sind alle Eingaben gemacht, geht es weiter zu Zeile 21, wo wir das `@nums`-Array mit der numerischen `sort`-Routine sortieren, die ich vorhin beschrieben habe. Beachten Sie, dass wir die Variablen `$a` oder `$b` nicht deklarieren brauchen - diese Variablen sind lokale Variablen von `sort` und fallen unter den Tisch, sobald die Sortierung vollständig ist. Zeile 22 berechnet den Durchschnitt wie in unserer vorigen Version des Skripts, und Zeile 23 errechnet den Median, also den Wert in der Mitte einer sortierten Liste. Dafür müssen wir lediglich den Wert von `$count` durch zwei teilen und das Ergebnis dann als Array-Index angeben (im Falle einer ungeraden Anzahl von Elementen ist das Ergebnis natürlich keine ganze Zahl, aber dann schneidet Perl die Nachkommastellen einfach ab).

Damit kommen wir zur Auswertung in den Zeilen 25 bis 30. Zähler, Summe und Durchschnitt sind dieselben wie vorher, aber jetzt haben wir auch Höchstwert, kleinsten und Zentralwert hinzugefügt. Maximum und Minimum sind einfach: Da unser Array ja sortiert ist, brauchen wir nicht einmal etwas berechnen, wir nehmen nur das erste und das letzte Element des Arrays. Und da wir den Median schon seit Zeile 23 kennen, müssen wir ihn nur noch ausgeben wie alle anderen Werte.

## Listen- und skalarer Kontext

Bevor wir die Arrays hinter uns lassen, möchte ich eine kleine Pause einlegen und das Thema Kontext behandeln. Kontext bezeichnet in Perl den Effekt, dass das Verhalten von Daten davon abhängt, was Sie mit ihnen zu machen versuchen. Viele an und für sich identische Daten »benehmen« sich je nach Kontext ganz unterschiedlich. Kontext kann sehr verwirrend sein, und wenn Sie damit durcheinandergeraten, können Sie Ergebnisse erhalten, die absolut sinnlos zu sein scheinen (oder Teile von Skripten anderer Programmierer produzieren scheinbar aus dem Nichts Resultate). Wenn Sie jedoch erst einmal verstanden haben, was Kontext in Perl bedeutet, und wissen, wie verschiedene Operationen damit arbeiten, können Sie auch komplexe Aufgaben sehr schnell erledigen, die in anderen Sprachen viele Zeilen Code erfordern würden.

> Wenn Sie ein erfahrener Programmierer sind und das Buch gerade nach Wichtigem durchsuchen, halten Sie genau hier an. Nehmen Sie sich die Zeit, diesen Abschnitt aufmerksam zu lesen und sicherzugehen, dass Sie es verstanden haben. Kontext kann sowohl Anfänger als auch erfahrene Perl-Programmierer in den Wahnsinn treiben.

### Was ist ein Kontext?

Was also bedeutet Kontext? Lassen Sie uns eine Analogie aus dem Deutschen verwenden: Nehmen Sie das Wort meinen. Definieren Sie es, in 25 Wörtern oder weniger.

Was ist die Definition des Worts meinen? Die richtige Antwort ist eigentlich: »Kommt drauf an, wie man es verwendet.« In dem Satz »Sie meinen nicht mich, sondern meinen Bruder« ist meinen einmal ein Verb (»Sie meinen«) und einmal ein Pronomen (»meinen Bruder«). »Sie meinen meinen Bruder« - dasselbe Wort, dieselbe Schreibweise, aber ganz verschiedene Bedeutungen, abhängig vom Zusammenhang - vom Kontext.

»Okay«, werden Sie sagen, »aber was hat das mit Perl zu tun?« Nehmen Sie den simplen Perl-Ausdruck `5 + 4`. Wozu wird dieser Ausdruck ausgewertet?

»Na, `9`«, sagen Sie und fragen sich, ob da ein Haken ist. Darauf können Sie wetten, es gibt einen: Was ist, wenn Sie diesen Ausdruck in eine Bedingung stellen, zum Beispiel so:

    if (5 + 4) { ... }

Wozu wird `5 + 4` jetzt ausgewertet? Gut, zuerst immer noch zu `9`. Aber weil wir es hier als Bedingung verwenden, wird es auch als wahr ausgewertet (Sie erinnern sich, nur `0` und `""` sind falsch). Im Kontext einer Bedingung - im Perl-Jargon Boolescher Kontext genannt - ist das Ergebnis von `5 + 4` ein Boolescher Wert, keine Zahl. Die `if`- Konstruktion erwartet als Ergebnis wahr oder falsch; Perl erkennt das und liefert wie (vom `if`, vielleicht gar nicht von Ihnen) gewünscht.

Auch die automatische Konvertierung zwischen Zahlen und Strings basiert auf Kontext. Bei Berechnungen erwartet Perl numerische Operanden (einen numerischen Kontext), deswegen konvertiert es alle Strings zu Zahlen. Stringoperatoren verlangen Stringkontext, und deshalb wandelt Perl numerische Werte in Strings um.

Numerischer, String- und Boolescher Kontext sind Formen des allgemeinen skalaren Kontexts. Um die Unterscheidung dieser drei brauchen Sie sich üblicherweise keine Sorgen zu machen, da Perl sie normalerweise erkennt und für Sie berücksichtigt.

Komplizierter wird es bei der Unterscheidung von Skalaren und Listen. Jede Operation in Perl wird entweder in skalarem oder in Listenkontext ausgewertet. Manche Perl-Operationen verlangen eindeutig skalaren, andere Listenkontext. Die Verwendung des falschen Datentyps würde bei diesen Operationen zu Fehlermeldungen führen. Doch eine dritte Sorte von Operationen kann sowohl in skalarem als auch in Listenkontext ausgewertet werden und sich in einem Kontext anders verhalten als in dem anderen. Und, um die Sache noch komplizierter zu machen, es gibt keine Standardregeln, wie Listen sich in skalarem Kontext verhalten und umgekehrt. Jede Operation hat ihre eigenen Regeln, und die müssen Sie sich merken oder nachsehen. Sobald Sie also mit einer Mischung aus Listen und Skalaren zu tun haben, sollten Sie sich für jede Operation in Perl drei Fragen stellen:

- In welchem Kontext bin ich (skalarer oder Listenkontext)?
- Welchen Datentyp verwende ich in diesem Kontext?
- Was passiert, wenn ich diesen Datentyp in diesem Kontext verwende?

Ich zeige Ihnen jetzt einige wenige Beispiele dieser »dritten Art«, in der Kontext so wichtig ist. Doch das Thema ist mit dieser Lektion nicht abgehakt, sondern fordert weiteren Fleiß Ihrerseits. Der Kontext spielt in Perl und in allen folgenden Lektionen so gut wie immer eine Rolle. Behalten Sie diese drei Fragen im Kopf und die Perl-Dokumentation griffbereit, dann ist es gar nicht so schwierig, wie es im Moment vielleicht scheint.

### Noch einmal: die Anzahl der Elemente in einem Array herausfinden

Ein Beispiel haben Sie bereits gesehen, in dem der Kontext entscheidend war. Erinnern Sie sich, wie man die Länge eines Arrays herausfindet?

    $anzahl_elemente = @array;

Ich habe Ihnen dazu gesagt, Sie sollen sich die Syntax einfach merken und sich keine Sorgen darum machen. Jetzt betrachten wir die Syntax genauer, denn es handelt sich um ein klassisches Beispiel, wie verwirrend Kontext in Perl sein kann. Die Variable `$anzahl_elemente` ist skalar, so dass die Zuweisung an diese Variable in skalarem Kontext ausgewertet wird - das heißt, auf der rechten Seite des `=`-Operators wird ein skalarer Wert erwartet (das ist die Antwort auf Ihre erste Frage: In welchem Kontext befinde ich mich? - In skalarem Kontext).

Auf der rechten Seite haben Sie eine Liste (das ist die Antwort auf die zweite Frage: Was für einen Datentyp benutze ich?). Also haben wir hier eine Liste in skalarem Kontext. Jetzt bleibt nur noch die Frage: »Was passiert mit einer Liste in diesem Kontext?« In diesem Fall wird die Anzahl von Elementen im Array `@array` der Variablen `$anzahl_elemente` zugewiesen. Wird eine Arrayvariable in skalarem Zuweisungskontext ausgewertet, liefert sie die Anzahl der Listenelemente.

Betrachten Sie diesen Vorgang nicht als Konvertierung der Liste in einen Skalar, denn das ist es nicht. Es findet keine Konvertierung statt. Die Liste benimmt sich in diesem Kontext einfach anders als sonst.

Überall, wo ein skalarer Wert erwartet wird und Sie eine Arrayvariable einfügen, erhalten Sie die Anzahl der Elemente dieses Arrays - zum Beispiel in jedem der folgenden Ausdrücke:

    $med = $array[@array / 2];    # Median, erinnern Sie sich?
    $total = @array1 + @array2 + @array3;
    if (@array > 10) { ... }
    while (@array) { ... }

Wenn Ihnen das verwirrend oder zu kryptisch aussieht, können Sie natürlich immer die simple Skalarzuweisung auf die eine und die Arrayvariable auf die andere Seite stellen. Oder wenn Sie die Länge des Arrays nur interessiert, weil Sie es mit einer Schleife durchlaufen möchten, können Sie die Frage nach der Anzahl der Elemente auch komplett umgehen und statt dessen `$#array` als Stoppwert verwenden.

### Kontext und Zuweisung

Zuweisungen sind der häufigste Fall, in dem der Kontext wichtig wird - oder zumindest ist Kontext hier am einfachsten zu erklären. Der Kontext auf der linken und der rechten Seite des Zuweisungsoperators kann passen (Skalar `=` Skalar, Liste `=` Liste), oder eben nicht (Skalar `=` Liste, Liste `=` Skalar). In diesem Abschnitt werde ich ein paar der einfachen Regeln durchgehen, wie Zuweisungen mit Kontext umgehen.

Lassen Sie uns mit den einfachen Fällen anfangen, in denen der Kontext paßt. Die haben Sie bereits alle kennengelernt. Skalar an Skalar bedeutet, ein einzelnes Objekt an ein einzelnes Objekt zuzuweisen, wobei Strings und Zahlen gegebenenfalls konvertiert werden:

    $x = 'foo';   # Skalarvariable, skalarer Wert

Auch Listen-an-Listen-Zuweisungen kennen Sie schon, sowohl mit Arrayvariablen auf der linken und Listensyntax auf der rechten Seite des Zuweisungsoperators als auch mit verschachtelten Listen:

    @zahlen = (10, 20, 30);
    ($x, $y, $z) = @zahlen;
    ($a, @zahlen2) = @zahlen;

Jetzt wollen wir uns die Fälle ansehen, in denen der Kontext nicht paßt. Sagen wir, Sie versuchen, einen Skalar in einem Listenkontext zuzuweisen, wie in diesen Beispielen:

    @array = 3.145;
    ($a, $b) = $c;

Das ist einfach! In diesen Fällen wird der skalare Wert rechts einfach in eine Liste umgewandelt und dann unter Verwendung der Listenzuweisungsregeln zugewiesen. Danach ist `@array` eine Liste mit einem einzigen Wert, `3.145`, `$a` hat den Wert von `$c`, und `$b` ist undefiniert.

Der vertrackteste Fall ist die Zuweisung einer Liste auf der rechten Seite an einen Skalar auf der linken. Sie haben gelernt, was passiert, wenn Sie eine Arrayvariable einer Skalarvariablen zuweisen:

    $anzahl_elemente = @array;

Das funktioniert mit jedem Array:

    $anzahl_elemente = sort @array;

Doch es funktioniert nur bei Arrays. Für ganz normale Listensyntax, also durch Kommas aufgeteilte Listen, ist die Regel anders:

    $x = (2, 4, 8, 16);

In diesem Fall werden - wie beim Komma-Operator in C - alle Werte in der Liste, außer dem letzten ignoriert. Der Wert von `$x` wird hier `16`. (Das ist etwas anderes als die Zuweisung derselben Liste an (`$x`) - eine Listen-an-Listen-Zuweisung würde beim ersten Element, `2`, beginnen und unbenutzte Elemente verwerfen.)

Das Wichtigste, was Sie sich merken sollten, ist, dass es keine allgemeine Regel gibt, wie eine Liste sich in skalarem Kontext verhält - Sie müssen die einzelnen Regeln kennen oder greifbar haben. Behalten Sie die drei Fragen im Kopf; dann dürften Sie keine Schwierigkeiten haben.

### Andere Kontexte

Es gibt noch ein paar andere Kontext-Situationen, die einer Betrachtung wert sind - und die Sie beim Arbeiten mit Listen und Skalaren beachten sollten.

Zuerst ist da der Boolesche Kontext, in dem eine Variable auf ihren Wahrheitswert überprüft wird (wie zum Beispiel im Bedingungsausdruck von `if` oder `while`). Sie haben schon gelernt, dass ein skalarer Wert in Booleschem Kontext wahr ist, wenn er irgendeinen Wert außer `""`, `0` oder `undef` hat.

Für Listen in Booleschem Kontext gilt eine ähnliche Regel - eine Liste mit irgendwelchen Elementen, einschließlich der undefinierten, ist in Booleschem Kontext wahr. Eine leere Liste ist falsch.

Zum zweiten ist der Kontext häufig sehr wichtig bei Funktionen. Viele Funktionen nehmen ihre Argumente als Liste entgegen und werten sie im Listenkontext aus. Wenn Sie Klammern um die Funktionsargumente setzen, wie wir gestern besprochen haben, ist das kein Problem - Sie übergeben der Funktion eine Liste mit Argumenten. Setzen Sie die Klammern aber nicht, versucht Perl, aus den gegebenen Argumenten selbst eine Liste zu bauen. Wenn diese Argumente aber Listen oder eingeklammerte Ausdrücke enthalten, kann Perl durcheinanderkommen. Betrachten wir ein Beispiel mit einer der Funktionen, die als Argument eine Liste erwarten, nämlich `print`:

    priNT 4 + 5, 6, 'foo';

Hier werden die auszugebenden Argumente ausgewertet, als wären sie die Liste `(9, 6, 'foo')`. Im folgenden Fall ist es allerdings anders:

    print (4 + 5), 6, 'foo';

Wegen der Klammern nimmt Perl an, dass der Ausdruck `4 + 5` das einzige Argument ist, und versteht nicht, was die `6` und das `'foo'` am Ende sollen. Aktivierte Perl- Warnungen würden diesen Fehler abfangen (und sich über die Verwendung von Konstanten in leerem Kontext beschweren). In solch einem Fall ist es am besten, die ganze Argumentenliste einzuklammern, damit nichts zweideutig ist:

    print ((4 + 5), 6, 'foo');

Meistens kann Perl erkennen, ob Klammern einen Funktionsaufruf, einen Ausdruck oder eine Liste meinen. In den meisten übrigen Fällen weisen die Warnungen Sie auf die Zweideutigkeiten hin. Aber achten Sie auf diese Unterschiede und den Kontext, wenn Sie Perl-Skripts schreiben.

### Die Funktion scalar

»Und was ist, wenn ich wirklich mal eine Liste im Skalarkontext verwenden möchte?« fragen Sie sich jetzt vielleicht. Keine Angst, Sie brauchen dafür keine langen Umwege (zum Beispiel eine temporäre Skalarvariable, nur um die Liste in skalaren Kontext zu bekommen). Mit der Funktion `scalar` können Sie eine Liste immer dazu zwingen, in skalarem Kontext ausgewertet zu werden. Nehmen Sie zum Beispiel die beiden folgenden Anweisungen:

    print @zahlen;
    print scalar(@zahlen);

Die `print`-Funktion wertet ihre Argumente im Listenkontext aus (deshalb können Sie mehrere durch Kommata getrennte Argumente angeben). Die erste dieser beiden Anweisungen expandiert nun das Array `@zahlen` in Listenkontext und gibt die Werte des Arrays aus. Die zweite wertet `@zahlen` in skalarem Kontext aus und liefert Ihnen in diesem Fall die Anzahl der Elemente von `@zahlen`.

## Eingabe, Ausgabe und Listen

Wie gestern betrachten wir zum Abschluß der Lektion einige Eingabe-/Ausgabe- Themen, diesmal mit besonderem Augenmerk auf den Listenkontext:

- `<STDIN>` im Listenkontext
- Ausgabe von Listen

### <STDIN> im Listenkontext

Gestern habe ich Ihnen gezeigt, wie man mit `<STDIN>` Daten von der Standardeingabe liest. Bis jetzt haben wir `<STDIN>` wie folgt verwendet:

    chomp($in = <STDIN>);

Bei genauem Betrachten erkennen Sie, dass wir `<STDIN>` hier in skalarem Kontext verwenden. Wie viele andere Perl-Operationen verhält sich auch der Eingabeoperator `<>` in Listenkontext anders als in skalarem.

In Skalarkontext liest `<STDIN>` eine Eingabezeile bis zum Zeilenvorschub. In Listenkontext aber liest `<STDIN>` immer weiter und speichert jede Zeile als ein Element in eine Liste, und zwar so lange, bis es zu einem Dateiende (end-of-file- )Zeichen kommt:

    @liste = <STDIN>;

Nun werden Sie fragen, wo Tastatureingaben denn ein Dateiendezeichen haben sollen. Setzen Sie es mit der Tastenkombination `[Strg]`-`[D]` (bzw. `[Strg]`-`[Z]` in Windows). Wenn Sie `<STDIN>` im Listenkontext verwenden, wartet Perl bei der Dateneingabe, bis Sie mit `[Strg]`-`[D]` bzw. `[Strg]`-`[Z]` sagen: »Hier ist das Dateiende«, speichert Ihre Daten dann in eine Liste und geht weiter im Skript.

Wirklich sinnvoll ist dieses Verhalten von `<STDIN>` in Listenkontext vor allem, wenn Sie aus Dateien lesen, also auch wirklich ein Dateiende haben. Für Tastatureingaben verwendet man normalerweise `<STDIN>` in skalarem Kontext. Aber es ist wichtig, sich auch für die Eingabe die Unterschiede zwischen skalarem und Listenkontext klarzumachen. Sie sind, wie bei so vielem in Perl, erheblich.

### Listen ausgeben

In den Beispielen dieses Kapitels haben wir zur Ausgabe von Listen mit Schleifen gearbeitet, die jedes Listenelement betrachtet und dann ausgegeben haben. Wenn Sie die Elemente einer Liste allerdings gar nicht verändern, sondern wirklich nur ausgeben möchten, gibt es einen leichteren Weg: Die `print`-Funktion geht grundsätzlich davon aus, dass ihre Argumente in Listenkontext stehen, und das können Sie ausnutzen. Nehmen wir zum Beispiel eine simple Liste von 1 bis 9:

    @liste = (1..9);

Wenn Sie diese Liste einfach mit `print @liste` ausgeben, erhalten Sie folgende Ausgabe:

    123456789

Es wird kein Zeilenvorschub ans Ende gesetzt. Die Werte der Liste werden lediglich verkettet, nichts weiter.

Wenn Sie die Elemente nun aber durch Leerzeichen getrennt ausgeben wollen? Sie könnten eine `while-` oder `foreach`-Schleife verwenden. Doch es geht auch einfacher - durch Variableninterpolation. Gestern haben wir die Variableninterpolation für Strings besprochen, durch die in einem String `"dies ist der String $zaehler"` die Variable `$zaehler` durch ihren aktuellen Wert ersetzt wurde. Variableninterpolation funktioniert auch mit Array- oder Hash-Variablen - die Inhalte des Arrays oder Hash werden dabei nacheinander, getrennt durch ein Leerzeichen, ausgegeben. Setzen Sie zum Beispiel `@liste` und einen Zeilenvorschub in Anführungszeichen:

    print "@liste\n";

Sie erhalten die Liste mit Leerzeichen zwischen den Elementen und einem Zeilenvorschub am Ende:

    1 2 3 4 5 6 7 8 9

Auch die Ausgabe von Hash-Variablen funktioniert auf diese Art, nur würde der Hash zuerst in seine Schlüssel und Werte zerlegt. Variableninterpolation von Listenvariablen macht es sehr einfach, die Inhalte einer Liste auszugeben, ohne auf Schleifen zurückgreifen zu müssen. Beachten Sie, dass Sie deswegen in einem double-quoted String vor ein `@`-Zeichen einen Backslash setzen müssen, damit Perl nicht glaubt, es wäre der Beginn einer Arrayvariablen und nach diesem gar nicht existierenden Array sucht (und sich dann beschwert, dass es keines findet). Perl-Warnungen geben Ihnen Bescheid, wenn Ihnen dieser Fehler unterläuft.

> Eine andere Möglichkeit, die Ausgabe von Listen zu steuern, besteht darin, spezielle globale Perl-Variablen für die Trennsymbole für Ausgabefelder (Output field separator) und Ausgabedatensätze (Output record separator) zu setzen. Mehr über diese speziellen Variablen erfahren Sie im Vertiefungsabschnitt.

## Vertiefung

Sie haben in dieser Lektion viel gelernt, doch es gibt noch einiges mehr über Arrays und Hashes, das ich noch nicht besprochen habe (wirklich!). Dieser Abschnitt faßt einige Eigenschaften von Listen, Arrays und Hashes zusammen, die ich im Hauptteil dieser Lektion nicht behandelt habe.

### Negative Array-Indizes

Im Array-Zugriffsausdruck `$array[index]` ist der (bei 0 beginnende) Index normalerweise die Position des Elements im Array. Sie können aber auch negative Array-Indizes verwenden, wie hier:

    $array[-1];

Negative Array-Indizes zählen vom Ende des Arrays zurück: Der Index `-1` bezieht sich auf die letzte Position im Array (genau wie `$#array`), `-2` auf die vorletzte und so weiter. Sie können mit negativen Indizes genauso auf die Elemente zugreifen wie mit positiven, obwohl es vielleicht aus Lesbarkeitsgründen die bessere Idee wäre, bei den positiven zu bleiben.

### Mehr über Bereiche

Wir haben in dieser Lektion mit dem Bereichsoperator `..` Zahlenlisten erstellt. Einige Eigenschaften von Bereichen habe ich Ihnen bis jetzt vorenthalten. So können Sie Bereiche zum Beispiel auch mit Buchstaben, genauer gesagt mit ASCII-Zeichen erzeugen - die Liste, die Sie erhalten, beginnt mit dem ersten Operanden, endet mit dem zweiten und enthält dazwischen alle Zeichen, die in der ASCII-Tabelle zwischen den beiden Operanden liegen. Das Ergebnis des Bereichs `'a .. z'` ist zum Beispiel eine Liste mit 26 Elementen, nämlich den Kleinbuchstaben von a bis z .

Sie können auch Zahlen und Buchstaben oder mehrere Buchstaben kombinieren, der Bereichsoperator liefert Ihnen auf geradezu magische Art und Weise die Werte zwischen den höheren und niedrigeren Werten.

Außerdem kann der Bereichsoperator auch in skalarem Kontext verwendet werden. Dann gibt er einen Booleschen Wert zurück, was in manchen Schleifen oder zur »Simulation« von `awk`- oder `sed`-ähnlichem Verhalten nützlich sein kann. Sehen Sie in der perlop-Manpage unter Range-Operator nach weiteren Informationen.

### chomp und chop mit Listen

Die Funktionen zum Entfernen von Zeilenvorschubs- oder anderen Zeichen vom Ende eines Strings, `chomp` und `chop`, akzeptieren auch eine Liste als Argument. In diesem Fall arbeiten sie sich durch alle Listenelemente und entfernen jeweils den Zeilenvorschub oder das letzte Zeichen von jedem Element. Das kann zum Beispiel nützlich sein, wenn Sie alle Zeilenvorschübe aus Daten entfernen möchten, die Sie aus einer Datei gelesen haben.

In der perlfunc-Manpage finden Sie weitere Informationen über `chomp` und `chop`.

### Trennsymbole für Ausgabefelder, Datensätze und Listen

In Perl ist ein Satz von globalen Spezialvariablen eingebaut, mit denen man Perls Verhalten in bestimmten Situationen steuern kann. Sie werden im Verlauf dieses Buchs viele dieser Variablen kennenlernen; eine vollständige Liste finden Sie auf der perlvar-Manpage.

Relevant für das heutige Thema sind die Variablen Output field separator (Trennsymbol für Ausgabefelder), Output record separator (Trennsymbol für Ausgabedatensätze) und List separator (Listentrennsymbol). Mit diesen drei globalen Variablen können Sie Perls Standardverhalten bei der Ausgabe von Listen verändern. Tabelle 4.1 erläutert diese Variablen.

Variable | Aufgabe, Voreinstellung
--- | ---
`$,` | Output field separator, Ausgabefeld-Trennsymbol: was zwischen Listenelementen ausgegeben wird. Voreinstellung ist leer, also kein Zeichen.
`$\` | Output record separator, Trennsymbol für Ausgabedatensätze: was am Ende einer Liste ausgegeben wird. Voreinstellung ist leer, also kein Zeichen.
`$"` | List separator, Listen-Trennsymbol: wie Output field separator, außer dass es nur auf Listenvariablen in interpolierten Strings angewendet wird. Voreinstellung ist ein einzelnes Leerzeichen.

Tabelle 4.1: Globale Ausgabevariablen

Wie Sie im Abschnitt »Listen ausgeben« gelernt haben, verkettet Perl alle Elemente einer Liste, wenn Sie die Liste ausgeben:

    print (1,2,3);  # gibt "123" aus

In Wirklichkeit setzt Perl den Wert des Ausgabefeld-Trennsymbols zwischen die Elemente und den des Ausgabedatensatz-Trennsymbols ans Ende der Liste. Da diese beiden Variablen standardmäßig leer sind, wird zwischen den Elementen und am Ende auch nichts ausgegeben - es sei denn, Sie verändern diese Variablen und legen zum Beispiel folgendes Ausgabeverhalten fest:

    $, = '*';
    $\ = "\n";
    print (1,2,3);  # gibt "1*2*3\n" aus

Das Listentrennsymbol bestimmt wie das Ausgabefeld-Trennsymbol aussieht, was zwischen Listenelementen ausgegeben wird, gilt aber nur für Listenvariablen in interpolierten Strings. Voreingestelltes Listentrennsymbol ist ein Leerzeichen - deswegen werden bei der Interpolation von Array- und Hash-Variablen standardmäßig Leerzeichen zwischen die Elemente gesetzt, wie Sie im Abschnitt »Listen ausgeben« gesehen haben. Möchten Sie dieses Ausgabeverhalten verändern, setzen Sie einfach das Listentrennsymbol auf einen Wert Ihrer Wahl.

### Leerer Kontext

Zusätzlich zu Listen-, skalarem und Booleschem Kontext kennt Perl auch einen leeren Kontext - das sind die Stellen, an denen Perl überhaupt nichts erwartet. Wenn Sie zum Beispiel einen String als Anweisung in Ihr Skript setzen:

    "das bringt nichts";

dann bringt das wirklich nichts - die einzige Auswirkung ist (bei aktivierten Perl- Warnungen) die Fehlermeldung `useless use of a constant in void context` (sinnlose Verwendung einer Konstante in leerem Kontext). Eine Fehlermeldung zu void context warnt Sie also, wenn Sie einen Ausdruck an einer Stelle verwenden, an der Perl gar nichts erwartet.

## Zusammenfassung

Heute war Listentag. Wie Sie heute gelernt haben, ist eine Liste einfach eine in Klammern gesetzte Reihe von durch Kommata getrennten Skalaren. Weisen Sie eine Liste einer Arrayvariablen `@array` zu, können Sie mit der Array-Zugriffssyntax `@array[index]` auf die einzelnen Elemente dieses Arrays zugreifen. Wir haben des weiteren besprochen, wie man die letzte Position im Array (`$#array`) und die Anzahl der Elemente (`$elements = @array`) herausfindet. Diesen Abschnitt haben wir mit ein paar Bemerkungen zu Listensyntax und Listenzuweisungen abgeschlossen, mit denen Sie Variablen auf beiden Seiten eines Zuweisungsausdrucks parallel Werte zuweisen können.

Dann sind wir das Thema Kontext angegangen, das eine wichtige Rolle spielt, weil Perl viele Dinge im Skalarkontext anders auswertet als im Listenkontext. Sie haben sich mit drei Fragen zum Verstehen eines Kontexts befaßt: Welcher Kontext wird in einem Ausdruck erwartet, welchen Datentyp haben die Daten, und was machen diese Daten in diesem Kontext?

Schließlich haben wir uns weiter mit einfacher Eingabe/Ausgabe, heute in bezug auf Listen, befaßt. Morgen werden wir Ihr Listengrundwissen mit der Behandlung von Hashes vervollständigen. (An Tag 19 werden wir noch fortgeschrittenere Datenstrukturen betrachten und dazu noch einmal auf die Listen zurückkommen - aber mit Listen, Arrays und Hashes im Perl-Repertoire können Sie schon eine Menge anstellen.)

Im folgenden noch einmal die Perl-Funktionen, die Sie heute kennengelernt haben:

- `qw` nimmt eine Liste von durch Leerzeichen getrennten Strings entgegen und gibt eine Liste der einzelnen Stringelementen zurück. `qw` erspart Ihnen das Eintippen vieler Anführungszeichen und Kommata, wenn Sie eine lange Liste von Strings zu definieren haben.
- `defined` nimmt eine Variable oder ein Listenelement entgegen und gibt wahr zurück, wenn der Wert an dieser Stelle nicht der undefinierte ist.
- `undef` nimmt eine Variable oder ein Listenelement entgegen und setzt sie/es auf den undefinierten Wert. Ohne Argumente gibt `undef` nur den undefinierten Wert zurück.
- `sort` nimmt eine Liste als Argument entgegen, sortiert diese Liste in ASCII- Reihenfolge und gibt die sortierte Liste zurück. Setzt man die Anweisung `{ $a <=> $b }` vor die Liste, sortiert es in numerischer Reihenfolge.
- `scalar` wertet eine Liste in Skalarkontext aus.

Mehr Informationen finden Sie in der perlfunc-Manpage bzw. im Anhang A.

## Fragen und Antworten

Frage:
Was ist der Unterschied zwischen dem undefinierten Wert und `undef`?

Antwort:
Der Unterschied ist gering. Der undefinierte Wert ist der Wert, den Perl Variablen, Array-Elementen oder Hash-Werten zuweist, wenn für sie kein Wert vorhanden ist - wenn Sie eine Variable verwenden, ohne sie zu initialisieren, oder wenn Sie die Länge eines Arrays vergrößern, ohne entsprechend Elemente einzufügen. Möchten Sie den undefinierten Wert explizit verwenden, zum Beispiel um eine Variable undefiniert zu machen oder den undefinierten Wert in ein Array aufzunehmen, nehmen Sie dafür die Funktion `undef`. Wegen der engen Beziehung zwischen dem undefinierten Wert und `undef` ist es weit verbreitet, dass `undef` für den undefinierten Wert steht (wie in »Die letzten drei Elemente dieses Arrays sind `undef`«). Sie werden in Ihrem Code nichts falsch machen, wenn Sie `undef` überall dort verwenden, wo Sie einen undefinierten Wert haben möchten.

Frage:
Ich möchte ein Array von Arrays erstellen.

Antwort:
Das können Sie nicht. Besser gesagt, jetzt noch nicht. Für Arrays von Arrays oder Arrays von Hashes oder jede Art von verschachtelten Datenstrukturen brauchen Sie Referenzen - und etwas Geduld. Wir werden Referenzen an Tag 19 behandeln.

Frage:
Oh je! Ich verstehe Skalar- und Listenkontext nicht. Wenn verschiedene Operationen verschiedene Dinge machen können und es keine Regeln gibt, wie Listen und Skalare sich im jeweils anderen Kontext verhalten, heißt das, dass ich mir für jede Operation merken muss, was sie in jedem Kontext macht?

Antwort:
Äh, ja. Nein. Naja, ungefähr. Wenn Sie die Frage nach dem Kontext absolut abscheulich finden, können Sie die esoterischeren Kontexterscheinungsformen in so gut wie jedem Skript weitestgehend vermeiden, merken Sie sich nur die wichtigsten Besonderheiten (wie man zum Beispiel die Länge eines Arrays herausfindet), und schlagen Sie den Rest nach, wenn irgend etwas nicht richtig funktioniert. Wenn Sie allerdings Skripts anderer Programmierer lesen, müssen Sie vermutlich auf den Kontext achten oder sogar nach verborgenem Kontext suchen.

Frage:
Ich möchte die Anzahl der Elemente in einer Liste herausfinden, also habe ich geschrieben »`length @array...`«

Antwort:
Hören Sie gleich hier auf. Die `length`-Funktion ist eine nette Funktion - für Strings und Zahlen. Um die Anzahl der Elemente in einer Liste herauszufinden, sollten Sie die Arrayvariable in skalarem Kontext auswerten: `$anzahl_elemente = @array`.

Frage:
Wie durchsuche ich ein Array nach einem bestimmten Element?

Antwort:
Eine Möglichkeit wäre, das Array mit einer `foreach-` oder `while`-Schleife zu durchlaufen und nacheinander jedes Element zu überprüfen. Perl hat aber auch eine Funktion namens `grep` (nach dem Unix-Suchbefehl), die das für Sie übernimmt. Am Tag 8 werden Sie mehr über `grep` erfahren.

## Workshop

Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.

## Quiz

1. Erklären Sie die Unterschiede zwischen Listen und Arrays.
2. Was ist das Ergebnis dieser Liste?

           @liste = (1, (), (4, 3), $foo, ((), 10, 5 + 4), (), (''));

3. Was bewirken die folgenden Perl-Anweisungen? Begründen Sie Ihre Antworten.

           ($x, $y, $z) = ('a', 'b');
         ($u, @mehr, $v) = (1 .. 10);
         $zahlen[4] = (1, 2, 3);  # @zahlen enthaelt anfangs (10,9,8)
         undef $zahlen[4];
         $zahlen[$#zahlen];
         $foo = @zahlen;
         @mehr = 4;

4. Nach welcher Regel wird eine Liste in einen Skalar konvertiert?
5. Wie sortiert man ein Array?
6. Was ist Unterschied zwischen `<STDIN>` in skalarem und in Listenkontext? Warum ist das wichtig?

## Übungen

1. Schreiben Sie ein Skript, das den Benutzer zur Eingabe von zwei Zahlen auffordert und dann ein Array aus den Zahlen dazwischen erstellt (der Benutzer soll sowohl die niedrigere als auch die höhere Zahl als erste eingeben können).
2. Schreiben Sie ein Skript, das Sie um zwei Arrays bittet und dann ein drittes Array erstellt, das nur die Elemente enthält, die in beiden anderen Arrays vorkommen (die Schnittmenge dieser Arrays).
3. FEHLERSUCHE: Was ist falsch an diesem Skript? (Tipp: Es könnten mehrere Fehler sein!)

           print 'Geben Sie eine Zahlenliste ein: ';
       chomp($in = <STDIN>);
       @zahlen = split(" ", $in);

       @sortiert = sort @zahlen;
       print "eingegebene Zahlen: @sortiert\n";

       $anz_zahlen = $#zahlen;
       print "Anzahl eingegebener Zahlen: $anz_zahlen\n";

## Antworten

Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.

### Antworten zum Quiz

1. Eine Liste ist einfach ein Satz von skalaren Elementen. Ein Array ist eine geordnete Liste, auf deren Positionen über Indizes zugegriffen werden kann.
2. Das Ergebnis der Liste ist: `(1, 4, 3, $foo, 10, 9, '')`. Dabei wird für `$foo` der aktuelle Wert von `$foo` eingesetzt.
3. Die Antworten sind:
4. a. `$x` wird `'a'`, `$y` wird `'b'`, `$z` wird undefiniert. Zuweisungen an Listen auf der linken Seite finden parallel statt, die Werte auf der rechten Seite werden den korrespondierenden Variablen auf der linken Seite zugewiesen.
5. b. `$u` wird `1`, `@mehr` wird `(2,3,4,5,6,7,8,9,10)`, `$v` wird undefiniert. Arrayvariablen auf der linken Seite einer Listenzuweisung sind gefräßig: Sie »schlucken« alle verbleibenden Werte auf der rechten Seite.
6. c. `$zahlen[4]` wird `3`. Beim Zuweisen einer rohen Liste an einen Skalar weist Perl nur den letzten Wert der Liste zu und ignoriert alle vorigen Werte.
7. Wenn der ursprüngliche Wert von `@zahlen` `(10,9,8)` war, ist der neue Wert von `@zahlen` jetzt `(10,9,8,undef,3)`. Wie gesagt werden bei Zuweisung von Listensyntax in skalarem Kontext alle Werte außer dem letzten ignoriert.
8. d. `$zahlen[4]` wird auf den undefinierten Wert gesetzt (vorher war es `3`).
9. e. `$zahlen[$#zahlen]` verweist auf den Wert an der letzten Position der Liste.
10. f. `$foo` enthält nach dieser Zuweisung die Anzahl der Elemente im Array `@zahlen`.
11. g. Die `4` wird zu einer Liste »befördert«, und `@mehr` wird eine Liste mit einem Element: `(4)`.
12. Fangfrage! Es gibt gar keine Regel für die Konvertierung einer Liste in einen Skalar. Außerdem gibt es eine solche Konvertierung überhaupt nicht. Listen verhalten sich in skalarem Kontext verschieden, je nachdem wie Sie sie verwenden.
13. Man sortiert ein Array mit der `sort`-Funktion:

            @sortiert = sort @array;

14. In skalarem Kontext liest `<STDIN>` eine Eingabezeile ein, die dann zu Ende ist, wenn der Benutzer die Eingabetaste drückt, und speichert diese Zeile in einer Skalarvariablen. In einem Listenkontext liest `<STDIN>` alle Zeilen von der Standardeingabe, bis es auf ein Dateiendezeichen stößt, und speichert jede Zeile als ein Element in einer Liste. Das sind zwei ganz verschiedene Arten, Daten in Ihr Programm einzulesen.

### Lösungen zu den Übungen

1. Hier eine mögliche Antwort:

           #!/usr/bin/perl -w

       $eins = 0;
       $zwei = 0;

       print 'Geben Sie eine Bereichsgrenze ein: ';
       chomp ($eins = <STDIN>);
       print 'Geben Sie die andere Bereichsgrenze ein: ';
       chomp ($zwei = <STDIN>);
       if ($eins < $zwei) {
           @array = ($eins .. $zwei);
       } else {
           @array = ($zwei .. $eins);
       }
       print "@array \n"

2. Hier eine Lösung, die von `foreach`-Schleifen Gebrauch macht:

           #!/usr/bin/perl -w

       $input = '' ; # Zwischenspeicher für Eingaben
       @array1 = (); # erstes Array
       @array2 = (); # zweites Array
       @final = ();  # Schnittmenge

       print 'Geben Sie das erste Array ein: ';
       chomp($input = <STDIN>);
       @array1 = split(' ', $input);
       print 'Geben Sie das zweite Array ein: ';
       chomp($input = <STDIN>);
       @array2 = split(' ', $input);

       foreach $el (@array1) {
           foreach $el2 (@array2) {
               if (defined $el2 && $el eq $el2) {
                   $final[$#final+1] = $el;
                   undef $el2;
                   last;
               }
           }
       }

       print "@final\n";

3. Anstatt der Zeile, die `$el` dem letzten Element im Array zuweist, könnten Sie auch die `push`-Funktion verwenden, die genau das gleiche tut (aber etwas einfacher zu lesen ist):

           push @final $el;

4. Fangfrage! Der einzige Fehler ist hier in der Zeile $anz_zahlen = $#zahlen. Die Annahme, dass `$#zahlen` die Anzahl der Elementen sei, ist falsch (`$#zahlen` liefert den höchsten Index, und der ist um eins kleiner als die Anzahl von Elementen). Verwenden Sie statt dessen:

       $anz_zahlen = @zahlen

> oder

    $anz_zahlen = $#zahlen + 1.

© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH