Das Kochrezept: Ein "Alltagsprogramm"

Wer noch nie in seinem Leben programmiert, wohl aber schon einmal nach Kochbuch etwas zubereitet hat, kann mit dieser Art der Beschreibung eines Prozesses etwas anfangen:

Griechische Tomatensuppe à la Almut

für 4 Portionen

Zutaten:

Zubereitung:

Sellerieknolle schälen und fein in 3 mm große Würfel schneiden. In Olivenöl unter ständigem Rühren in einem Topf goldgelb andünsten. Mit Wasser ablöschen, zum Kochen bringen. Brühwürfel und Tomatenmark einrühren. Tomaten aus der Dose kleinschneiden in 1 cm Würfel, evtl. Haut entfernen. Tomatensaft durch grobes Sieb gießen, um eventuelle Hautreste abzufischen.

Alles in den Topf geben (Für Bequeme: Einfach gewürfelte Dosentomaten hinzufügen).

Mit buntem Pfeffer, Salz, Chili oder Tabasco pikant abschmecken.

Vorsicht: Wenn die Suppe länger durchgezogen ist, verstärkt sich die Schärfe noch!

Alles aufkochen lassen. Dann die Nudeln hinzugeben und mindestens 15 Minuten kochen lassen.

Anschließend noch die gehackte Petersilie unterrühren.

Kali Orezi (griech.: Guten Appetit)!

An sich sind wir schon näher an der Schreibweise eines Programms dran, als wir vielleicht vermuten würden. So ist der Teil "Zutaten" klar vom Teil "Zubereitung" abgetrennt.

Auch in LSL werden uns solche Aufstellungen von Zutaten begegnen, nur dass sie dort globale Variablen heißen. "Global" meint hier, dass diese - wie auch im oben genannten Rezept für die gesamte Suppe - im gesamten Script gelten. Damit wird ein Unterschied zu lokalen Variablen gemacht, die nur in einem bestimmten Unterabschnitt des Scripts gültig sind. Im Rezept sehen wir so etwas auch, nämlich wenn ohne vorherige Erwähnung in der Zutatenliste gesagt wird: "Sellerieknolle (...) in 3 mm große Würfel schneiden" Wir wüssten sofort, was zu tun ist. Für ein Script hingegen müsste der Wert "3 mm" als gesonderte lokale Variable festgelegt werden, damit klar wird, wie genau beim Schneiden des Selleries verfahren werden soll.

Des weiteren sehen wir, dass die jeweiligen Zutaten auch nur in den für sie sinnvollen Größenangaben angegeben werden. Die Älteren unter uns erinnern sich vielleicht noch an Didi Hallervordens Sketch "Ich hätte gern eine Flasche Pommes Frites...", die in den Worten eines Programmierers keine sinnvolle Variablendeklaration wäre!

So finden wir in der Zutatenliste "große Dose", "Brühwürfel", "Esslöffel" und "Prise". In LSL entsprächen diese Angaben den für die Variablendeklaration möglichen sogenannten Datentypen wie "string" (Zeichenkette), "integer" (Ganzzahl: 0, 1, 2...) und "float" (Fließkommazahl, in LSL - wie in der EDV-Welt allgemein üblich - allerdings mit Trennpunkt geschrieben: 2.4, 42.3218 etc.). Doch auch Kombinationen gleicher Variablen sind möglich: Der Datentyp "vector" ist ein Container für drei, "rotation" sogar für vier Float-Werte. Man kann außerdem mehrere unterschiedliche Datentypen, die man aus bestimmten Gründen zusammenhalten möchte, in einer "list" (Liste) speichern. Im Beispiel unseres Kochrezepts käme für so etwas beispielsweise die Tomatendose in Frage, die bei näherem Hinsehen gleich sehr viele unterschiedliche Eigenschaften umfasst:

list dosenTomaten = ["Fruchtfleisch", "Haut", "Saft", 0.850];

In der Fließkommazahl 0.850 ist die nur sehr ungefähre Angabe "große Dose" präzisiert. Als versierte Hausfrau (oder Hausmann) weiß man, was eine große Dose ist. Einem Programm muss man aber immer alles ganz haarklein erklären und irgendwo auch eine einheitliche Bezugsgröße - hier: Liter - festlegen.

Einige Angaben, die ich kursiv dargestellt habe, sind lediglich Kommentare, die für den Ablauf des Prozesses selbst zwar nicht entscheidend sind, wohl aber dem Koch wertvolle ergänzende Mitteilungen liefern. Solche Kommentare gibt es auch in Programmiersprachen; sie werden besonders gekennzeichnet und bei der Ausführung des Programms schlichtweg ignoriert. Sie kosten keine Rechenzeit und verursachen damit auch kein Lag. Daher sei gerade am Anfang die großzügige Verwendung von Kommentaren empfohlen! In LSL geschrieben sähe so ein Kommentar beispielsweise so aus (man beachte die beiden Schrägstriche am Anfang!):

//für Bequeme: 2 kleine Dosen gewürfelte Tomaten

Nun zeichnen sich Programmiersprachen auch dadurch aus, dass sie nur eine sehr stark formalisierte Form der Eingabe akzeptieren. Jede kreative (oder schusselige!) Abweichung wird mit einer Fehlermeldung bestraft. Meistens sind es aber nur Kleinigkeiten wie vergessene Semikola am Ende, so dass man sich darum erst einmal nicht allzu viele Sorgen machen muss.

Betrachten wir nun einmal den zweiten Block unseres Rezepts - die Zubereitung - gewissermaßen von innen nach außen:

Das, was mit den Zutaten geschehen soll, wird durch Funktionen geregelt. Diese Anweisungen - oft auch als "Befehle" bezeichnet - bringen das eigentliche Leben ins Programm. Sie werden in allen Programmiersprachen immer sehr allgemeingültig ausgedrückt, damit sie möglichst universell eingesetzt werden können. Gäbe es beispielsweise eine Funktion für das Kleinschneiden von Zutaten, wäre es unerheblich, ob es sich dabei um Sellerie, Tomaten, Gurken, Bananen oder Lakritzstangen handelt. Sehr wohl würde man die Information zu den zu schneidenden Lebensmitteln der Funktion in einem Parameter zuordnen. Dieser Parameter würde dann nicht nur die Art des Schneidguts beinhalten, sondern vielleicht noch weitere Informationen mitliefern. So könnte man noch als weitere Parameter übergeben, wie geschnitten werden soll (Scheiben, Würfel etc.) und in welcher Größe. All dies würde man in den unsichtbaren Tiefen hinter einer solchen Funktion verbergen, so dass man auch nicht jedesmal wieder extra angeben müsste, wie und mit was man schneiden muss. Unsere fiktive Küchenfunktion könnte also so aussehen:

schneideZutat (Sellerieknolle, Wuerfel, 3);

Bitte beachten, dass Funktionen wie Variablendeklarationen immer mit einem Semikolon abgeschlossen werden. Dasselbe gilt übrigens auch für Operationen aller Art, die man mit seinen Variablen anstellen kann (Additionen, Vergleiche usw.).

In LSL findet man eine Besonderheit, die so nicht alle Programmiersprachen bieten: Um überhaupt die Ausführung eines Programmteils zu veranlassen, bedarf es eines auslösenden Ereignisses oder Events. Dies kann entweder von außen - also durch uns oder einen anderen Avatar - erfolgen oder innerhalb des Scripts durch eine entsprechende Funktion angestoßen werden. Man denke beispielsweise an eine Zeitschaltuhr oder Timer, den man beim Kochen zum Überwachen einmaliger oder wiederholter Vorgänge einsetzen kann. Es kann aber auch sein, dass der Koch etwas aktiv veranlassen muss, damit ein bestimmter Block von Funktionen abgearbeitet wird. Muss er beispielsweise die Küchenmaschine zum Kleinschneiden des Gemüses anschalten, könnte dieser Funktionsblock mit einem so auch schon im echten LSL verwendbaren Event eingeleitet werden:

touch_start (integer total_number)
{//Hier würden die Funktionen stehen}

Was sagt uns das genau? Es beschreibt die Art der Handlung, nämlich dass etwas bei Beginn der Berührung ausgelöst wird. Wie bei den Funktionen gibt es auch hier Parameter in Klammern, die aber nicht von uns definierte Variablen benötigen, sondern vom Server geliefert werden. Dieser zählt als Ganzzahl (daher "integer"), wie oft ein Objekt (hier: Der An-/Ausschalter der Küchenmaschine) betätigt worden ist. Ob man mit diesen Parametern etwas machen möchten oder diese einfach links liegen lassen, hängt vom Zweck des Scripts, der Art des Events und seiner Funktionen ab. Bei manchen sind diese Server-Parameter sehr wichtig. In jedem Falle gehören sie aber zu einem Event wie das Salz in die Suppe. Ein Weglassen oder die Verwendung anderer Datentypen würden zu einer Fehlermeldung führen.

Die große Klammer um all diese Events mit ihren Funktionen bildet ein sogenannter State, was so viel wie "Zustand" bedeutet. Der vorgegebene Standard-State in LSL heißt "default" und umfasst alles, was seine geschweiften Klammern umfassen. Man kann aber noch mehr als einen State in einem Script ausweisen, wenn einem das sinnvoll erscheint, und mit einem frei gewählten Namen bedenken. Es muss nur das Wort "state" stets vorangestellt werden. In unserem Beispiel aus der Küche könnte der state Zubereitung heißen, sollte man default bereits fürs Einkaufen verbraucht haben. Ansonsten würde man alles im Standard-State unterbringen, was im weit überwiegenden Teil aller Scripte der Fall seit dürfte:

default
{//Hier würden die Events mit all ihren Funktionen stehen}

Das war jetzt aber doch etwas unübersichtlich, oder? Deshalb noch einmal die Grundstruktur eines LSL-Scripts in einer zusammenfassenden Übersicht:

//Globale Variablen (= "Zutaten"):
Datentyp GlobalVariable1 = Wert; //Variable mit Wertzuweisung.
Datentyp GlobalVariable2; //Variable noch ohne Wertzuweisung.

//Erster (und hier einziger) State mit Standardbenennung (= "Zubereitung"):
default
{
//Ereignis, bei dessen Eintreffen die folgenden Funktionen ausgeführt werden:
einEvent (Datentyp Server-Variablenname) //Variablenname kann verändert werden, nicht aber der Typ; auch Weglassen ist nicht erlaubt.
{
GlobalVariable2 = Wert; //Zuweisung eines Wertes zu einer globalen Variable.
LokalVariable = Wert; //Einführung einer nur in diesem Event (!) geltenden lokalen Variable und Zuweisung eines Wertes.
//Funktion, die mittels der übergebenen Variablen eine bestimmte Aktion ausführt:
eineFunktion (GlobalVariable1, GlobalVariable2, LokalVariable); //Runde Klammern umfassen die Parameter, welche die Funktion benötigt.
}
}

Jedes Script muss mindestens einen State und mindestens ein Event umfassen. Globale Variablen hingegen sind kein Muss, und auch ein Script ganz ohne Funktionen würde ohne Fehlermeldung abgespeichert werden. Bei einem Kochrezept wäre das aber gleichbedeutend mit einer Aufforderung zum Fasten, so dass wir diesen Sonderfall getrost ausschließen können.

Dergestalt vorbereitet, machen wir uns nun an unser allererstes echtes Script namens "Hello Avatar!".

© 2012 ff. Almut Brunswick. Nur für den persönlichen Gebrauch! Aus rechtlichen Gründen distanziere ich mich von allen externen Links.