3.3. Listen, Listen und noch mehr Listen

Wir haben Ihnen etwas über Variablen und Funktionen erklärt, und nun betreten wir die düsteren Sümpfe von Schemes Listen...

3.3.1. Eine Liste definieren

Bevor wir mehr über Listen erzählen, müssen Sie den Unterschied zwischen atomaren Werten und Listen kennen.

Sie haben bereits atomare Werte gesehen, als wir im vorherigen Abschnitt Variablen initialisiert hatten. Ein atomarer Wert ist ein einzelner Wert. Zum Beispiel können wir in der folgenden Anweisung der Variablen »x« den Wert 8 zuweisen:

(let* ( (x 8) ) x)

(Wir haben den Ausdruck x am Ende hinzugefügt, um den zugewiesen Wert auszugeben – normalerweise brauchen Sie das nicht. Beachten Sie, dass sich let* genau wie eine Funktion verhält: Der Wert der letzten Anweisung ist der Rückgabewert.)

Eine Variable kann sich auch auf eine Liste von Werten statt auf einen einzelnen Wert beziehen. Um der Variablen x die Liste der Werte 1, 3, 5 zuzuweisen, schreiben wir:

(let* ( (x '(1 3 5))) x)

Probieren Sie beide Anweisungen in der Skript-Fu-Konsole aus und achten Sie auf die Antworten. Bei der ersten Anweisung lautet sie einfach:

8

Wenn Sie aber die andere Anweisung eingeben, ist die Antwort das folgende Ergebnis:

(1 3 5)

Die erste Antwort sagt Ihnen, dass x den atomaren Wert 8 enthält. Aber wenn die Antwort (1 3 5) lautet, dann heißt das, dass x keinen atomaren Wert, sondern eine Liste von Werten enthält. Beachten Sie, dass weder in unserer Deklaration oder Zuweisung der Liste noch im ausgegebenen Resultat Kommas stehen.

Die Syntax, um eine Liste zu definieren, lautet:

'(a b c)

wobei a, b und c Literale (buchstabengetreu einzugeben) sind. Wir verwenden den Apostroph (') um anzuzeigen, dass der nachfolgende Klammerausdruck eine Liste literaler Werte ist und nicht eine Funktion oder ein Ausdruck.

Eine leere Liste kann wie folgt definiert werden:

'()

oder einfach:

()

Listen können sowohl atomare Werte als auch andere Listen enthalten:

(let*
   (
        (x
           '("GIMP" (1 2 3) ("ist" ("toll" () ) ) )
        )
    )
    x
)
      

Beachten Sie, dass Sie nach dem ersten Apostroph keine weiteren Apostrophe mehr brauchen, wenn Sie innere Listen definieren. Probieren Sie es nun aus, kopieren Sie die Anweisung in die Skript-Fu-Konsole und schauen Sie, was sie zurück liefert.

Sie sollten bemerkt haben, dass das zurückgelieferte Ergebnis keine Liste einzelner, atomarer Werte ist. Statt dessen ist es eine Liste eines Literals "GIMP", der Liste (1 2 3) etc.

3.3.2. Wie man sich Listen vorstellen sollte

Es ist hilfreich, sich Listen zusammengesetzt aus einem »Kopf« (head) und einem »Rest« (tail, wörtlich »Schwanz« oder »Ende«) vorzustellen. Der Kopf ist das erste Element der Liste, der Rest alle folgenden Elemente. Sie werden gleich sehen, warum das wichtig ist, wenn wir besprechen, wie man zu Listen etwas hinzufügt und wie man auf Elemente der Liste zugreift.

3.3.3. Listen durch Konkatenation (Verkettung) erstellen (die cons-Funktion)

Eine der üblichen Funktionen, auf die Sie treffen werden, ist die cons-Funktion. Sie nimmt einen Wert und fügt ihn vor dem zweiten Argument (eine Liste) ein. Im vorigen Abschnitt habe ich vorgeschlagen, sich die Liste aus einem Element (head) und dem Rest (tail) zusammengesetzt vorzustellen. Genau so funktioniert cons – diese Funktion fügt ein Element als Listenkopf ein. Somit können Sie eine Liste wie folgt erstellen:

(cons 1 '(2 3 4) )

Das Ergebnis ist die Liste (1 2 3 4).

Sie könnten auch eine Liste mit nur einem Element erstellen:

(cons 1 () )

Sie können vorher deklarierte Variablen anstelle irgendwelcher Literale verwenden, genau so, wie man es erwarten würde.

3.3.4. Eine Liste mit der list-Funktion definieren

Um eine Liste aus Literalen oder vorher deklarierten Variablen zu definieren, verwenden Sie die list-Funktion:

(list 5 4 3 a b c)

Das wird eine Liste erstellen, die die Werte der Variablen a, b und c enthält, und diese zurückgeben. Beispiel:

        (let*  (
                  (a 1)
                  (b 2)
                  (c 3)
               )

               (list 5 4 3 a b c)
        )
      

Dieser Code erzeugt die Liste (5 4 3 1 2 3).

3.3.5. Auf Werte in einer Liste zugreifen

Um auf Werte in einer Liste zuzugreifen, verwenden Sie die Funktionen car und cdr, die das erste Element resp. den Rest einer Liste liefern. Diese Funktionen gliedern die Liste in die vorher erwähnte Struktur aus head (Kopf) und tail (Rest) auf.

3.3.6. Die car-Funktion

car liefert das erste Element (den Kopf) einer nicht-leeren Liste. Folgende Anweisung liefert also das erste Element der Liste:

(car '("erstes" 2 "drittes"))

nämlich:

"erstes"

3.3.7. Die cdr-Funktion

cdr liefert den Rest der Liste nach dem ersten Element. Wenn die Liste nur ein Element enthält, liefert cdr eine leere Liste.

(cdr '("erstes" 2 "drittes"))

liefert:

(2 "drittes")

während folgende Anweisung:

(cdr '("das einzig wahre"))

liefert:

()

3.3.8. Auf andere Elemente der Liste zugreifen

Na schön, wir können sowohl auf das erste Element als auch auf den Rest der Liste zugreifen, aber wie kommen wir an das zweite, dritte oder irgendein anderes Element der Liste? Aus praktischen Gründen gibt es einige Zugriffsfunktionen, um beispielsweise auf den Kopf eines Kopfes eines Rests (caadr) oder den Rest eines Restes (cddr) einer Liste zuzugreifen.

Die zugrunde liegende Namenskonvention ist einfach: Die »a«s und »d«s repräsentieren Köpfe beziehungsweise Reste von Listen, d.h.

(car (cdr (car x) ) )

kann geschrieben werden als:

(cadar x)

Um mit den Listenzugriffsfunktionen etwas Übung zu bekommen, versuchen Sie mal, folgendes einzugeben (alles in einer Zeile, falls Sie die Konsole benutzen) und verschiedene Variationen von cars und cdrs auszuprobieren, um auf verschiedene Elemente der Liste zuzugreifen:

        (let* (
                 (x  '( (1 2 (3 4 5) 6)  7  8  (9 10) )
                 )
              )
              ; hierhin kommt Ihr car/cdr-Code
        )
      

Versuchen Sie, mit nur zwei Funktionsaufrufen auf die Zahl 3 zuzugreifen. Wenn Sie das schaffen, sind Sie auf dem besten Wege, ein Skript-Fu-Meister zu werden!

[Anmerkung] Anmerkung

In Scheme kennzeichnet ein Semikolon (»;«) einen Kommentar. Das Semikolon und alles folgende in derselben Zeile wird vom Scheme-Interpreter ignoriert. Sie können das also verwenden, um Kommentare hinzuzufügen und Ihrem Gedächtnis auf die Sprünge zu helfen, wenn Sie sich das Skript später anschauen.