Dieses Dokument dient als Schnellreferenz für alle Funktionen der Programmiersprache Verse und ihrer Syntax. Folge den Links, um mehr zu erfahren.
Ausdrücke
Ein Ausdruck ist die kleinste Code-Einheit, die ein Ergebnis erzeugt, wenn sie ausgewertet wird. Ein Beispiel ist ein if ... else
-Ausdruck, der in Verse zu einem Wert ausgewertet wird, der vom Inhalt der Ausdrucksblöcke abhängt.
Der folgende Code ergibt einen String-Wert, der entweder "Big!"
enthält. oder "Small!"
, abhängig davon, ob MyNumber
größer war als 5:
if (MyNumber > 5):
"Big!"
else:
"Small!"
Codekommentare
Ein Codekommentar erklärt etwas über den Code oder den Grund des Programmierers, warum etwas so programmiert ist, wie es ist. Wenn das Programm ausgeführt wird, werden Codekommentare ignoriert.
Verse
| Einzeiliger Kommentar: Alles, was zwischen |
Verse
| Inline-Block-Kommentar: Alles, was zwischen |
Verse
| Mehrzeiliger Kommentar: Alles, was zwischen |
Verse
| Geschachtelter Blockkommentar: Alles, was zwischen |
Verse
| Eingerückter Kommentar: Alles, was auf neuen Zeilen nach |
Konstanten und Variablen
Konstanten und Variablen können Informationen oder Werte speichern, die dein Programm verwendet, und diese Werte mit einem Namen verbinden. Der Name ist die Kennung.
Um deine eigene Variable oder Konstante zu erstellen, musst du Verse darüber informieren. Dies wird als Deklaration bezeichnet. Die Angabe eines Anfangswertes, Initialisierung genannt, ist für Variablen optional (aber empfohlen), für Konstanten jedoch erforderlich.
Du kannst den Wert einer Variablen jederzeit ändern. Dies wird formal als Zuweisung bezeichnet, weil du der Variablen einen Wert zuweist, wird aber manchmal auch als Setzen der Variablen bezeichnet.
Verse
| Erstellen einer Konstante: Der Wert einer Konstanten kann nicht geändert werden, während das Programm läuft. Du erstellst eine Konstante, indem du ihren Namen, Typ und Wert angibst. | Klicke auf das Bild, um es zu vergrößern. |
Verse
| Erstellen einer Variablen: Der Wert einer Variablen kann geändert werden, während das Programm läuft. Du erstellst eine Variable, indem du das Schlüsselwort | Klicke auf das Bild, um es zu vergrößern. |
Verse
| Wert einer Variablen ändern: Du kannst den Wert einer Variablen während der Ausführung des Programms mit dem Schlüsselwort | Klicke auf das Bild, um es zu vergrößern. |
Typen
Verse ist eine statisch-typisierte Programmiersprache, was bedeutet, dass jeder Kennung ein Typ zugewiesen wird.
Es gibt Fälle, in denen die Angabe des Typs nicht ausdrücklich erforderlich ist, z. B. bei der Erstellung einer Konstante in einer Funktion. Im Beispiel MyConstant := 0
wird der Typ für MyConstant
abgeleitet, weil ihm der Wert 0 zugewiesen wird.
Gängige Typen
Verse hat integrierte Typen, die die grundlegenden Operationen unterstützen, die die meisten Programme ausführen müssen. Du kannst deine eigenen Typen erstellen, indem du diese zu größeren Strukturen kombinierst, aber diese allgemeinen Typen sind wichtig, um die Grundlage für die Verwendung von Variablen und Konstanten in Verse zu verstehen.
Verse
| logic: Ein |
Verse
| int: Ein
|
Verse
| float: Ein
|
Verse
| Ein Mit {} innerhalb der "" kannst du das Ergebnis eines Ausdrucks in einen String einfügen. Dies wird als String-Interpolation bezeichnet. Um mehr über den Typ |
Verse
| message: Eine Du kannst den Text zur Laufzeit in einen anzeigbaren String umwandeln, indem du die Funktion Derzeit ist der einzige Text, der von der Funktion |
Verse
| locale: Dieser Typ gibt an, in welchem Kontext ein Derzeit ist der Typ |
Verse
| rational: Der Typ
|
Verse
| void: Der Typ
Um mehr über den Typ |
Verse
| any: Der Typ |
Verse
| comparable: Der Typ |
Um mehr über gängige Typen in Verse zu erfahren, siehe Gängige Typen.
Containertypen
Du kannst mehrere Werte zusammen speichern, indem du einen Containertyp verwendest. Verse hat eine Reihe von Containertypen, in denen du Werte speichern kannst. Um mehr über Containertypen in Verse zu erfahren, siehe Containertypen.
Option
Der Typ option
kann einen Wert enthalten oder leer sein.
Im folgenden Beispiel ist MaybeANumber
eine optionale Ganzzahl ?int
, die keinen Wert enthält. Ein neuer Wert für MaybeANumber
wird dann auf 42
gesetzt.
var MaybeANumber : ?int = false # unset optional value
set MaybeANumber := option{42} # assigned the value 42
Klicke auf das Bild, um es zu vergrößern.
Verse
| Erstellen einer Option: Du kannst eine Option mit einer der folgenden Möglichkeiten initialisieren:
Gib den Typ an, indem du |
Verse
| Zugriff auf ein Element in einer Option: Verwende den Abfrageoperator |
Nachfolgend ein Beispiel für die Verwendung eines Optionstyps, um einen Verweis auf einen gespawnten Spieler zu speichern und, wenn ein Spieler gespawnt wird, das Triggergerät reagieren zu lassen:
my_device := class<concrete>(creative_device):
var SavedPlayer : ?player = false # unset optional value
@editable
PlayerSpawn : player_spawner_device = player_spawner_device{}
@editable
Trigger : trigger_device = trigger_device{}
OnBegin<override>() : void =
Bereich
Der range-Ausdruck enthält alle Zahlen in einem angegebenen Bereich und kann nur in bestimmten Ausdrücken verwendet werden, wie im for-Ausdruck. Bereichswerte können nur Ganzzahlen sein.
Klicke auf das Bild, um es zu vergrößern.
Verse
| Erstellen eines Bereichs: Der Beginn des Bereichs ist der erste Wert im Ausdruck, und das Ende des Bereichs ist der Wert, der auf |
Verse
| Iterieren über einen Bereich: Du kannst den Ausdruck |
Weiter Informationen findest du unter Bereich.
Array
Ein Array ist ein Container, in dem Elemente desselben Typs gespeichert werden können. Der Zugriff auf die Elemente erfolgt über ihre Position, den sogenannten Index, im Array. Der erste Index im Array ist 0, und der letzte Index ist um eins kleiner als die Anzahl der Elemente im Array.
Players : []player = array{Player1, Player2}
Klicke auf das Bild, um es zu vergrößern.
Verse
| Erstellen eines Arrays: Verwende das Schlüsselwort |
Verse
| Zugriff auf ein Element in einem Array: Verwende |
Verse
| Ändern eines Elements in einem Array: Du kannst den Wert, der in einer Array-Variablen an einem Index gespeichert ist, mit dem Schlüsselwort |
Verse
| Iterieren über ein Array: Du kannst auf jedes Element in einem Array zugreifen, in der Reihenfolge vom ersten zum letzten, indem du den Ausdruck for benutzt. Mit dem Ausdruck |
Verse
| Anzahl der Elemente in einem Array ermitteln: Verwende |
Verse
| Verkettung von Arrays: Du kannst Arrays zusammenfügen, indem du den |
Weiter Informationen findest du unter Array.
Tupel
Ein Tupel ist eine Gruppe von zwei oder mehr Ausdrücken, die wie ein einziger Ausdruck behandelt wird. Die Reihenfolge der Elemente im Ausdruck ist wichtig. Derselbe Ausdruck kann an mehreren Stellen in einem Tupel stehen. Tupel-Ausdrücke können von beliebigem Typ sein und können gemischte Typen haben (im Gegensatz zu Arrays, die nur Elemente eines Typs haben können).
Tupel sind besonders nützlich, wenn:
Rückgabe mehrerer Werte aus einer Funktion.
Übergabe mehrerer Werte an eine Funktion.
Die Gruppierung an Ort und Stelle ist übersichtlicher als der Aufwand, eine wiederverwendbare Datenstruktur (wie eine Struktur oder Klasse zu erstellen.
VerseExampleTuple : tuple(int, float, string) = (1, 2.0, "three")
Klicke auf das Bild, um es zu vergrößern.
Verse
| Erstellen eines Tupels: Verwende |
Zugriff auf ein Element in einem Tupel: Verwende | |
Tupel-Erweiterung: Wenn du ein Tupel als Argument für eine Funktion verwendest, anstatt jedes einzelne Argument anzugeben, wird die Funktion mit jedem Element des Tupels als Argument in der Reihenfolge aufgerufen, in der sie im Tupel angegeben sind. | |
Tupel-Array-Zwang: Tupel können überall dort übergeben werden, wo ein Array erwartet wird, vorausgesetzt, die Typen der Tupel-Elemente sind alle vom gleichen Typ wie das Array. Matrizen können nicht übergeben werden, wenn ein Tupel erwartet wird. |
Weiter Informationen findest du unter Tupel.
Karte
Eine Map ist ein Container, in dem du Werte speichern kannst, die mit einem anderen Wert verknüpft sind, sogenannte Schlüssel-Wert-Paare, und über deren eindeutige Schlüssel du auf die Elemente zugreifen kannst.
WordCount : [string]int = map {"apple" => 11, "pear" => 7}
Klicke auf das Bild, um es zu vergrößern.
Erstellen einer Map: Verwende das Schlüsselwort | |
Zugriff auf ein Element in einer Map: Verwende | |
Iterieren über eine Map: Du kannst auf jedes Element in einer Map zugreifen, in der Reihenfolge vom ersten bis zum letzten eingefügten Element, indem du den Ausdruck for benutzt. Mit dem | |
Anzahl der Schlüssel-Wert-Paare in einer Map ermitteln: Verwende |
Weiter Informationen findest du unter Map.
Zusammengesetzte Typen
Ein Zusammengesetzter Typ ist ein Typ, der aus Feldern und Elementen (in der Regel benannt) von primitiven oder anderen Typen bestehen kann. Zusammengesetzte Typen haben normalerweise eine feste Anzahl von Feldern oder Elementen für ihre Lebensdauer. Weitere Informationen findest du unter Zusammengesetzte Typen.
Aufzählung
Enum ist die Abkürzung für enumeration (Aufzählung), was bedeutet, dass eine Reihe von Dingen, die Aufzähler genannt werden, benannt oder aufgelistet werden. Dies ist ein Typ in Verse, der für Dinge wie Wochentage oder Himmelsrichtungen verwendet werden kann.
Klicke auf das Bild, um es zu vergrößern.
Erstellen einer Aufzählung: Verwende das Schlüsselwort | |
Zugriff auf eine Aufzählung: Verwende |
Struct
Struct, kurz für Structure (Struktur), ist eine Möglichkeit, verschiedene verwandte Variablen zu gruppieren. Die Variablen können jeden beliebigen Typ aufweisen.
Klicke auf das Bild, um es zu vergrößern.
Klicke auf das Bild, um es zu vergrößern.
Erstellen einer Struct: Verwende das Schlüsselwort | |
Instanzieren einer Struct: Du kannst eine Instanz einer Struct aus einem Archetyp konstruieren. Ein Archetyp definiert die Werte der Felder einer Struct. | |
Zugriff auf Felder in einer Struktur: Du kannst auf die Felder einer Struktur zugreifen, um ihren Wert zu erhalten, indem du |
Klasse
Eine Klasse ist eine Vorlage zum Erstellen von Objekten mit ähnlichen Verhaltensweisen und Eigenschaften (Variablen und Methoden) und muss instanziert sein, um ein Objekt mit echten Werten zu erstellen. Klassen sind hierarchisch, was bedeutet, dass eine Klasse Informationen von ihren Parents (Superklasse) erben und ihre Informationen mit ihren Children (Subklassen) teilen kann. Eine Klasse kann ein vom Benutzer definierter benutzerdefinierter Typ sein.
Klicke auf das Bild, um es zu vergrößern.
Klicke auf das Bild, um es zu vergrößern.
Erstellen einer Klasse: Definitionen, die innerhalb einer Klasse verschachtelt sind, definieren Felder der Klasse. Funktionen, die als Felder einer Klasse definiert sind, werden auch als Methoden bezeichnet. | |
Instanzieren einer Klasse: Du kannst eine Instanz der Klasse konstruieren, indem du den Klassennamen gefolgt von | |
Zugriff auf Felder einer Klasse: Du kannst auf die Felder einer Klasse zugreifen, um ihren Wert zu erhalten, indem du | |
Zugriff auf Methoden einer Klasse: Du kannst auf die Methoden einer Klasse zugreifen, um sie aufzurufen, indem du | |
Self: Du kannst |
Es gibt auch Spezifizierer, die nur für Klassen gelten und deren Verhalten verändern. Siehe Klassenbezeichner für weitere Informationen.
Um mehr über Klassen zu erfahren, siehe Klasse.
Subklasse
Eine Subklasse ist eine Klasse, die die Definition einer anderen Klasse erweitert, indem sie die Felder und Methoden der anderen Klasse (Superklasse genannt) hinzufügt oder ändert.
Klicke auf das Bild, um es zu vergrößern.
Erstellen einer Subklasse: Gib eine Klasse als Subklasse einer anderen Klasse an, indem du die andere Klasse zwischen den | |
Felder in der Superklasse überschreiben: Du kannst die Werte eines Feldes, das in der Superklasse definiert ist, nur für die Subklasse ändern, indem du den Bezeichner | |
Methoden in der Superklasse überschreiben: [INCLUDE:#overriding_methods_on_superclass_description] | |
Super: Ähnlich wie bei |
Weiter Informationen findest du unter Subklasse.
Constructor
Ein Konstruktor ist eine spezielle Funktion, die eine Instanz der Klasse erzeugt, mit der sie verbunden ist. Er kann dazu verwendet werden, Anfangswerte für das neue Objekt festzulegen.
Definieren eines Constructors für eine Klasse: Du kannst einen Constructor für eine Klasse hinzufügen, indem du dem Funktionsnamen den Bezeichner | |
Hinzufügen von Variablen und Ausführen von Code im Constructor: Du kannst Ausdrücke innerhalb eines Konstruktors mit dem block-Ausdruck ausführen, und neue Variablen mit dem Schlüsselwort | |
Aufruf anderer Constructors in einem Constructor: Du kannst andere Constructors von einem Constructor aus aufrufen. Du kannst auch Konstruktoren für die Oberklasse der Klasse von einem Konstruktor der Klasse aufrufen, solange alle Felder initialisiert sind. Wenn ein Konstruktor einen anderen Konstruktor aufruft und beide Konstruktoren Felder initialisieren, werden nur die dem ersten Konstruktor bereitgestellten Werte für die Felder verwendet. Die Reihenfolge der Auswertung von Ausdrücken zwischen den beiden Konstruktoren entspricht der Reihenfolge, in der die Ausdrücke geschrieben werden (was die Seiteneffekte betrifft), aber es werden nur die Werte verwendet, die dem ersten Konstruktor übergeben werden. |
Interface
Der Typ `Interface` bietet einen Vertrag darüber, wie mit jeder Klasse, die das Interface implementiert, interagiert werden soll. Ein Interface kann nicht instanziiert werden, aber eine Klasse kann von dem Interface erben und seine Methoden implementieren. Ein Interface ist ähnlich wie eine abstrakte Klasse, außer dass sie keine teilweise Implementierung oder Felder als Teil der Definition zulässt.
Klicke auf das Bild, um es zu vergrößern.
Klicke auf das Bild, um es zu vergrößern.
Interface erstellen: Ein Interface wird ähnlich wie eine Klasse definiert, jedoch mit dem Schlüsselwort | |
Interface erweitern: Ein Interface kann die Definition eines anderen Interfaces erweitern, indem sie das zu erweiternde Interface zwischen den | |
Interface implementieren: Du kannst ein Interface mit einer Klasse implementieren, indem du das Interface zwischen den | |
Mehrere Interfaces implementieren: Eine Klasse kann mehrere Interfaces implementieren. Die Interfaces werden durch | |
Erben von einem Interface und einer anderen Klasse: Eine Klasse kann ein Interface implementieren und eine Subklasse einer anderen Klasse sein. Interface und Superklasse werden durch |
Weiter Informationen findest du unter Interface.
Arbeiten mit Typen
Verse bietet einige Möglichkeiten, um die Arbeit mit Typen zu erleichtern.
Typ-Aliasing: Du kannst einem Typ in deinem Code einen anderen Namen geben und den Typ mit diesem neuen Namen referenzieren. Die Syntax ist ähnlich wie bei der Initialisierung von Konstanten. Du kannst einem Funktionstyp auch einen Typ-Alias geben. Weitere Informationen findest du unter Typ-Aliasing. | |
Parametrische Typen als explizite Typargumente: Verse unterstützt parametrische Typen (Typen, die als Argumente erwartet werden). Dies funktioniert nur bei Klassen und Funktionen. Weitere Informationen findest du unter Parametrische Typen. | |
Parametrische Typen als implizite Typargumente für Funktionen: Der Grund für die Verwendung impliziter parametrischer Typen mit Funktionen ist, dass du damit Code schreiben kannst, der für einen bestimmten Typ einmal invariant ist und nicht für jeden Typ, mit dem die Funktion verwendet wird. Weitere Informationen findest du unter Parametrische Typen. | |
Typ-Makro: Verse hat ein spezielles Konstrukt, das verwendet werden kann, um den Typ eines beliebigen Ausdrucks zu erhalten. Er kann überall genutzt werden, wo ein Typ verwendet werden kann. Weitere Informationen findest du unter Typ-Makro. | |
Subtyp: Du kannst |
Weitere Informationen findest du unter Arbeiten mit Verse-Typen.
Operatoren
Operatoren sind spezielle Funktionen, die in der Programmiersprache Verse definiert sind und Aktionen, wie z. B. mathematische Operationen, mit ihren Operanden durchführen.
Wenn mehrere Operatoren im selben Ausdruck verwendet werden, werden sie in der Reihenfolge des höchsten bis niedrigsten Vorrangs ausgewertet. Wenn es im selben Ausdruck Operatoren mit der gleichen Rangfolge gibt, werden sie von links nach rechts ausgewertet.
In der folgenden Tabelle sind alle eingebauten Operatoren in Verse aufgeführt, in der Reihenfolge vom höchsten zum niedrigsten Vorrang.
Bediener | Beschreibung | Operatorenformat | Vorrang des Operators | Beispiel |
---|---|---|---|---|
Abfrage | Der Operator | postfix | 9 |
|
Nicht | Der Operator | Präfix | 8 |
|
Positiv | Du kannst den Operator | Präfix | 8 |
|
Negativ | Du kannst den Operator | Präfix | 8 |
|
Multiplikation | Das | Infix | 7 |
|
Division | Der Operator | Infix | 7 |
|
Addition | Der Operator + addiert zwei Zahlenwerte. Wenn du sie mit Zeichenketten und Arrays verwendest, werden die beiden Werte verkettet. Siehe Math für weitere Details. | Infix | 6 |
|
Subtrakion | Der Operator | Infix | 6 |
|
Addition-Zuweisung | Mit diesem Operator kannst du Addition und Zuweisung in der gleichen Operation kombinieren, um den Wert einer Variablen zu aktualisieren. Siehe Math für weitere Details. | Infix | 5 |
|
Subtraktion-Zuweisung | Mit diesem Operator kannst du Subtraktion und Zuweisung in der gleichen Operation kombinieren, um den Wert einer Variablen zu aktualisieren. Siehe Math für weitere Details. | Infix | 5 |
|
Multiplikation-Zuweisung | Mit diesem Operator kannst du Multiplikation und Zuweisung in der gleichen Operation kombinieren, um den Wert einer Variablen zu aktualisieren. Siehe Math für weitere Details. | Infix | 5 |
|
Division-Zuweisung | Mit diesem Operator kannst du Division und Zuweisung in der gleichen Operation kombinieren, um den Wert einer Variablen zu aktualisieren, es sei denn, die Variable ist eine ganze Zahl. Siehe Math für weitere Details. | Infix | 5 |
|
Ist gleich | Der Operator | Infix | 4 |
|
Ist nicht gleich | Der | Infix | 4 |
|
Kleiner als | Der Operator | Infix | 4 |
|
Kleiner als oder gleich | Der Operator | Infix | 4 |
|
Größer als | Der Operator | Infix | 4 |
|
Größer als oder gleich | Der Operator | Infix | 4 |
|
Und | Der Operator | Infix | 3 |
|
Oder | Der Entscheidungsoperator | Infix | 2 |
|
Initialisierung von Variablen und Konstanten | Mit diesem Operator kannst du Werte in einer Konstanten oder Variable speichern. Siehe Konstanten und Variablen für weitere Details. | Infix | 1 |
|
Variablenzuweisung | Mit diesem Operator kannst du die in einer Variablen gespeicherten Werte aktualisieren. Siehe Konstanten und Variablen für weitere Details. | Infix | 1 |
|
Weitere Informationen findest du unter Operatoren.
Gruppierung
Du kannst die Reihenfolge, in der die Operatoren ausgewertet werden, ändern, indem du Ausdrücke mit ()
gruppierst. Beispielsweise ergeben (1+2)*3
und 1+(2*3)
nicht denselben Wert, da die in () zusammengefassten Ausdrücke zuerst ausgewertet werden.
Das folgende Beispiel zeigt, wie die Gruppierung verwendet wird, um eine Explosion im Spiel zu berechnen, die ihren Schaden auf der Grundlage der Entfernung zum Spieler skaliert, bei der jedoch die Rüstung des Spielers den Gesamtschaden reduzieren kann:
BaseDamage : float = 100.0
Armor : float = 15.0
DistanceScaling : float = Max(1.0, Pow(PlayerDistance, 2.0))
ExplosionDamage : float = Max(0.0, (BaseDamage / DistanceScaling) - Armor)
Siehe Gruppierung für weitere Details.
Code-Blocks
Ein Codeblock ist eine Gruppe von null oder mehr Ausdrücken und führt einen neuen Geltungsbereich-Körper ein. Ein Codeblock muss auf einen Bezeichner folgen. Dies kann eine Funktionskennung oder eine Kontrollflusskennung sein, wie zum Beispiel if und for.
Eingerückt: Beginnt mit | |
Mehrzeilig umklammert: Umgeben von | |
Einzeilig umklammert: Eingeschlossen von |
Es ist auch möglich, ;
zu verwenden, um mehr als einen Ausdruck in eine Zeile zu setzen. In einem Format, bei dem jeder Ausdruck in einer neuen Zeile steht, müssen die {}
-Zeichen nicht in einer eigenen Zeile stehen.
Der letzte Ausdruck in einem Codeblock ist das Ergebnis des Codeblocks. Im folgenden Beispiel ergibt der Codeblock des if
-Ausdrucks entweder false
, wenn IsLightOn?
erfolgreich ist, oder true
, wenn IsLightOn?
fehlschlägt. Das logic
-Ergebnis wird dann in NewLightState
gespeichert.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Weitere Informationen findest du unter Code-Blöcke.
Bereich
Geltungsbereich bezieht sich auf den Code innerhalb eines Verse-Programms, in dem die Zuordnung einer Kennung (Name) zu einem Wert gültig ist und in dem diese Kennung verwendet werden kann, um auf den Wert zu verweisen.
Beispielsweise existieren alle Konstanten oder Variablen, die du innerhalb eines Codeblocks erstellst, nur im Kontext dieses Codeblocks. Das bedeutet, dass die Lebensdauer von Objekten auf den Geltungsbereich beschränkt ist, in dem sie erstellt werden, und sie können nicht außerhalb dieses Codeblocks verwendet werden.
Das folgende Beispiel zeigt, wie man die maximale Anzahl an Pfeilen berechnet, die man mit der Anzahl an Münzen kaufen kann, die der Spieler hat. Die Konstante MaxArrowsYouCanBuy
wird innerhalb des if
-Blocks erstellt, so dass der Geltungsbereich auf den if
-Block beschränkt ist. Wenn die Konstante MaxArrowsYouCanBuy
in der Druck-String verwendet wird, erzeugt sie einen Fehler, weil der Name MaxArrowsYouCanBuy
außerhalb des if
-Ausdrucks im Geltungsbereich nicht existiert.
CoinsPerQuiver : int = 100
ArrowsPerQuiver : int = 15
var Coins : int = 225
if (MaxQuiversYouCanBuy : int = Floor(Coins / CoinsPerQuiver)):
MaxArrowsYouCanBuy : int = MaxQuiversYouCanBuy * ArrowsPerQuiver
Print("You can buy at most {MaxArrowsYouCanBuy} arrows with your coins.") # Error: Unknown identifier MaxArrowsYouCanBuy
Klicke auf das Bild, um es zu vergrößern.
Verse unterstützt nicht die Wiederverwendung einer Kennung, selbst wenn er in einem anderen Bereich deklariert ist, es sei denn, du qualifizierst die Kennung, indem du (qualifying_scope:)
vor der Kennung hinzufügst, wobei qualifying_scope
der Name eines Moduls, einer Klasse oder eines Interface der Kennung ist. Wann immer du den Bezeichner definierst und verwendest, musst du an den Bezeichner auch einen Qualifizierer anhängen.
Funktionen
Eine Funktion ist eine benannte Folge von Ausdrücken, die du wiederverwenden kannst. Eine Funktion enthält Anweisungen zur Durchführung einer Aktion oder zur Erzeugung einer Ausgabe auf der Grundlage einer Eingabe.
Funktionsdefinitionen
Um deine eigene Funktion zu definieren, musst du drei Hauptbestandteile angeben: einen eindeutigen Namen (Bezeichner), die Art der Informationen, die du als Ergebnis erwarten kannst, und was die Funktion tun wird, wenn du sie aufrufst. Dies ist die grundlegende Syntax einer Funktion:
name() : type =
codeblock
Der Name() und der Typ, getrennt durch einen Doppelpunkt: Dies ist die Funktionssignatur, d. h. wie du die Funktion aufrufen und verwenden musst. Der Wert, der von der Funktion zurückgegeben werden muss, hat den von dir angegebenen Typ. Dieses Format ähnelt der Erstellung von Konstanten, mit Ausnahme des () nach dem Namen, das den Aufruf der Funktion in deinem Code nachahmt.
Funktions-Codeblock: Du definierst, was die Funktion tun soll, wenn sie aufgerufen wird, indem du
=codeblock
angibst, wobeicodeblock
eine beliebige Folge von einem oder mehreren Ausdrücken ist. Wenn du die Funktion aufrufst, werden die Ausdrücke im Codeblock ausgeführt.
Klicke auf das Bild, um es zu vergrößern.
Funktionsergebnisse
Wenn in deiner Funktion ein Rückgabetyp angegeben ist, muss der Funktionskörper ein Ergebnis dieses Typs liefern, sonst lässt sich der Code nicht kompilieren.
Letzter Ausdruck, der mit einem Wert zurückgegeben wird: Standardmäßig ist der letzte Ausdruck im Codeblock der Funktion das Ergebnis der Funktion, dessen Wert mit dem Rückgabetyp der Funktion übereinstimmen muss. | |
Explizite Rückgabe mit einem Wert: Du kannst auch explizit definieren, was die Funktion zurückgeben soll, indem du |
Wenn du eine Funktion erstellst, die kein Ergebnis liefern muss, kannst du den Rückgabetyp der Funktion auf void setzen, was bedeutet, dass von der Funktion kein nützliches Ergebnis erwartet wird und der letzte Ausdruck im Codeblock der Funktion von beliebigem Typ sein kann.
Du kannst eine Funktion, deren Rückgabetyp void
ist, beenden, indem du den Ausdruck return
isoliert verwendest. Der Ausdruck `return` beendet die Funktion sofort, auch wenn im Codeblock weitere Ausdrücke folgen. |
Funktionsparameter
Die Eingabe einer Funktion wird mit Parametern definiert. Ein Parameter ist eine Konstante, die in der Funktionssignatur zwischen den Klammern deklariert wird und die du dann im Hauptteil der Funktion verwenden kannst.
Es folgt die Syntax für eine Funktion mit zwei Parametern:
name(parameter1name : type, parameter2name : type) : type =
codeblock
Klicke auf das Bild, um es zu vergrößern.
Im folgenden Beispiel hat die Funktion IncreaseScore()
einen ganzzahligen Parameter namens Points
, den die Funktion verwendet, um den Wert von MyScore
zu erhöhen:
var MyScore : int = 100
IncreaseScore(Points : int) : void =
# Increase MyScore by Points.
set MyScore = MyScore + Points
Funktionsaufrufe
Wenn du die benannte Folge von Ausdrücken (die Funktion) in deinem Code verwenden willst, rufst du die Funktion mit ihrem Namen auf, z. B. GetRandomInt(1, 10)
, die eine zufällige ganze Zahl zwischen 1 und 10 einschließlich zurückgibt.
Es gibt zwei Möglichkeiten, eine Funktion aufzurufen, je nachdem, ob der Funktionsaufruf fehlbar ist:
Nicht-fehlbarer Funktionsaufruf: Ein Funktionsaufruf, der nicht fehlschlagen kann, hat die Form | |
Fehlbarer Funktionsaufruf: Ein fehlbarer Funktionsaufruf hat die Form |
Funktionsargumente
Wenn du eine Funktion aufrufst, die Parameter erwartet, musst du den Parametern Werte zuweisen, so wie du auch Konstanten Werte zuweisen musst, um sie verwenden zu können. Die zugewiesenen Werte werden Argumente der Funktion genannt.
Es folgt die Syntax für eine Funktion mit zwei Argumenten:
name(parameter1name := value, parameter2name := value)
Im folgenden Beispiel wird die Funktion IncreaseScore()
dreimal aufgerufen, jedes Mal mit anderen Argumenten (10, 5 und 20), um den Wert von MyScore
zu erhöhen:
# After this call, MyScore is 110
IncreaseScore(Points := 10)
# After this call, MyScore is 115
IncreaseScore(Points := 5)
# After this call, MyScore is 135
IncreaseScore(Points := 20)
Erweiterungsmethoden
Erweiterungsmethoden sind eine Art von Funktionen, die sich wie Mitglieder einer bestehenden Klasse oder eines Typs verhalten, aber nicht die Erstellung eines neuen Typs oder einer Subklasse erfordern.
Im Folgenden wird gezeigt, wie man eine Erweiterungsmethode für Arrays vom Typ int
erstellt. Die Methode addiert alle Zahlen im Array und gibt die Summe zurück.
# Sum extension method for type []int
(Arr : []int).Sum<public>() : int =
var Total : int = 0
for (Number : Arr):
set Total = Total + Number
return Total
Die Methode kann dann mit jedem Array vom Typ int
aufgerufen werden.
SumTotal := array{4, 3, 7}.Sum()
Print("The SumTotal is { SumTotal }")
# "The SumTotal is 14"
Fehlschlag
Im Gegensatz zu anderen Programmiersprachen, die die booleschen Werte true und false verwenden, um den Ablauf eines Programms zu ändern, verwendet Verse Ausdrücke, die entweder erfolgreich sein oder fehlschlagen können. Diese Ausdrücke werden fehlbare Ausdrücke genannt. Sie können nur in einem Fehlerkontext ausgeführt werden.
Fehlbare Ausdrücke
Ein fehlbarer Ausdruck ist ein Ausdruck, der entweder erfolgreich ist und einen Wert liefert oder fehlschlägt und keinen Wert zurückgibt.
Die folgende Liste enthält alle fehlbaren Ausdrücke in Verse:
Funktionsaufrufe: Nur wenn der Funktionsaufruf die Form | |
Vergleich: Ein Vergleichsausdruck vergleicht zwei Dinge unter Verwendung von einem der Vergleichsoperatoren. Weitere Informationen findest du unter Operatoren. | |
Ganzzahlige Division: Für ganze Zahlen ist der Divisionsoperator | |
Entscheidung: Ein Entscheidungsausdruck verwendet die Operatoren | |
Abfrage: Ein Abfrageausdruck verwendet den Operator | |
Zugriff auf ein Element in einem Array: Der Zugriff auf den in einem Array gespeicherten Wert ist ein fehlbarer Ausdruck, da es an diesem Index möglicherweise kein Element gibt, und muss daher in einem Fehlerkontext verwendet werden. Weitere Details findest du unter Array. |
Fehlerkontexte
Ein Fehlerkontext ist ein Kontext, in dem fehlbare Ausdrücke ausgeführt werden dürfen. Der Kontext definiert, was passiert, wenn der Ausdruck fehlschlägt. Jeder Fehlschlag innerhalb eines Fehlerkontexts führt dazu, dass der gesamte Kontext fehlschlägt.
Mit einem Fehlerkontext können verschachtelte Ausdrücke Fehlerausdrücke sein, wie zum Beispiel Funktionsargumente oder Ausdrücke in einem block-Ausdruck.
Die folgende Liste enthält alle Fehlerkontexte in Verse:
Die Bedingung in | |
Iterations- und Filterausdrücke in | |
Der Körper einer Funktion, die den Effektbezeichner | |
Der Operand für den | |
Der linke Operand für den | |
Initialisierung einer Variablen, die den |
Spekulative Ausführung
Ein nützlicher Aspekt von Fehlerkontexten in Verse ist, dass sie eine Form der spekulativen Ausführung darstellen, dass du also Aktionen ausprobieren kannst, ohne sie festzulegen. Wenn ein Ausdruck erfolgreich ist, werden die Auswirkungen des Ausdrucks festgelegt, z. B. Ändern des Werts einer Variablen. Wenn der Ausdruck fehlschlägt, wird sein Effekt zurückgenommen, als wäre der Ausdruck nicht verwendet worden.
So kannst du eine Reihe von Aktionen ausführen, deren Änderungen akkumuliert werden, die jedoch zurückgenommen werden, wenn sie irgendwo im Fehlerkontext fehlschlagen.
Damit dies funktioniert, müssen alle Funktionen, die im Fehlerkontext aufgerufen werden, den Effektbezeichner transacts
haben.
Spezifizierer
Spezifizierer in Verse beschreiben das Verhalten in Bezug auf die Semantik und können zu Bezeichnern und bestimmten Schlüsselwörtern hinzugefügt werden. Die Syntax des Bezeichners verwendet <
und >
mit dem Schlüsselwort dazwischen, wie z. B. IsPuzzleSolved()<decides><transacts>: void
.
In den folgenden Abschnitten werden alle Spezifizierer und Attribute in Verse beschrieben und es wird erläutert, wann du sie verwenden kannst.
Effekt-Bezeichner
Effekte in Verse zeigen Kategorien von Verhalten an, die eine Funktion zeigen darf. Du kannst Effekt-Spezifizierer hinzufügen zu:
Das () nach dem Namen in einer Funktionsdefinition:
name()<specifier> : type = codeblock
.Das Schlüsselwort
class
:name := class<specifier>():
.
Effekt-Spezifizierer sind in zwei Kategorien aufgeteilt:
Exklusiv: Du kannst nur einen oder keinen der exklusiven Effekt-Bezeichner zu einer Funktion oder zum Schlüsselwort
class
hinzufügen. Wenn kein exklusiver Effekt hinzugefügt wird, ist der Standardeffektno_rollback
.Additiv: Du kannst alle, einige oder keinen der additiven Effekt-Bezeichner zu einer Funktion oder dem Schlüsselwort
class
hinzufügen.
Beispiel | Effekt |
---|---|
no_rollback: Dies ist der Standardeffekt, wenn kein exklusiver Effekt hinzugefügt wird. Der Effekt | |
Exklusive Effekte | |
transacts: Dieser Effekt zeigt an, dass alle von der Funktion durchgeführten Aktionen rückgängig gemacht werden können. Der Transacts-Effekt ist immer dann erforderlich, wenn eine veränderbare Variable ( | |
varies: Dieser Effekt zeigt an, dass die gleichen Inputs in die Funktion nicht immer den gleichen Output erzeugen. Der Effekt | |
computes: Dieser Effekt setzt voraus, dass die Funktion keine Nebenwirkungen hat und es wird nicht garantiert, dass sie abgeschlossen wird. Es gibt eine ungeprüfte Anforderung, dass die Funktion, wenn sie mit den gleichen Argumenten versehen wird, das gleiche Ergebnis liefert. Jede Funktion, die nicht den native-Bezeichner hat, die sonst den | |
converges: Dieser Effekt garantiert nicht nur, dass es keine Nebenwirkung bei der Ausführung der entsprechenden Funktion gibt, sondern auch, dass die Funktion definitiv abgeschlossen wird (nicht unendlich rekursiv). Dieser Effekt kann nur in Funktionen auftreten, die den native-Bezeichner haben, aber dies wird vom Compiler nicht überprüft. Code, der verwendet wird, um Standardwerte von Klassen oder Werte für globale Variablen bereitzustellen, muss diesen Effekt haben. Die Funktionen | |
Zusatzeffekte | |
decides: zeigt an, dass die Funktion fehlschlagen kann und dass der Aufruf dieser Funktion ein fehlbarer Ausdruck ist. Funktionsdefinitionen mit dem Effekt | |
suspends: Zeigt an, dass die Funktion asynchron ist. Erzeugt einen asynchronen Kontext für den Körper der Funktion. |
Der Aufruf einer Funktion, die einen bestimmten Effekt hat, setzt in jedem Fall voraus, dass auch der Aufrufer diesen Effekt hat.
Zugriffsbezeichner
Zugriff-Spezifizierer definieren, was mit einem Mitglied interagieren kann und wie. Zugriff-Spezifizierer können auf Folgendes angewendet werden:
Die Kennung für ein Mitglied:
name<specifier> : type = value
Das Schlüsselwort
var
für ein Mitglied:var<specifier> name : type = value
Klassenbezeichner
Klassenbezeichner definieren bestimmte Eigenschaften von Klassen oder deren Mitgliedern, z. B. ob du eine Unterklasse einer Klasse erstellen kannst.
abstract: Wenn eine Klasse oder eine Klassenmethode den Bezeichner abstract hat, kannst du keine Instanz der Klasse erstellen. Abstrakte Klassen sind dazu bestimmt, als Oberklasse mit teilweiser Implementierung oder als gemeinsames Interface verwendet zu werden. Dies ist nützlich, wenn es keinen Sinn macht, Instanzen einer Oberklasse zu haben, du aber keine Eigenschaften und Verhaltensweisen in ähnlichen Classes duplizieren willst. | |
concrete: Wenn eine Klasse den Bezeichner concrete hat, muss es möglich sein, sie mit einem leeren Archetyp zu konstruieren, was bedeutet, dass jedes Feld der Klasse einen Standardwert haben muss. Außerdem ist jede Unterklasse einer konkreten Klasse selbst konkret. Eine konkrete Class kann nur dann direkt von einer abstrakten Class erben, wenn beide Classes in demselben Modul definiert sind. | |
unique: Der Bezeichner unique kann auf eine Klasse angewendet werden, um sie zu einer einzigartigen Klasse zu machen. Um eine Instanz einer eindeutigen Klasse zu erstellen, weist Verse der entstehenden Instanz eine eindeutige Identität zu. So können Instanzen von eindeutigen Klassen auf Gleichheit verglichen werden, indem ihre Identitäten verglichen werden. Klassen ohne den `unique`-Spezifizierer haben keine solche Identität und können daher nur auf der Grundlage der Werte ihrer Felder auf Gleichheit verglichen werden. Das bedeutet, dass eindeutige Classes mit den Operatoren „=“ und „<>“ verglichen werden können und Untertypen des Typs „comparable“ sind. |
Implementierungsbezeichner
Es ist nicht möglich, Implementierungs-Spezifizierer beim Schreiben von Code zu verwenden, aber du wirst diese sehen, wenn du dir die UEFN-APIs ansiehst.
native_callable: Gibt an, dass eine Instanzmethode sowohl nativ (in C++ implementiert) ist als auch von anderem C++-Code aufgerufen werden kann. Diesen Spezifizierer siehst du verwendet für eine Instanzmethode. Dieser Bezeichner wird nicht an Subklassen weitergegeben, so dass du ihn nicht zu einer Definition hinzufügen musst, wenn du eine Methode überschreibst, die diesen Bezeichner hat. |
Lokalisierungsbezeichner
Du musst den Bezeichner localizes
verwenden, wenn du eine neue Nachricht definierst. Dies gilt insbesondere, wenn die Variable den Typ message hat und du die Variable mit einem string-Wert initialisierst.
# The winning player's name:
PlayerName<localizes> : message = "Player One"
# Build a message announcing the winner.
Announcement<localizes>(WinningPlayerName : string) : message = "...And the winner is: {WinningPlayerName}"
Billboard.SetText(Announcement("Player One"))
Du musst den Bezeichner localizes
nicht verwenden, wenn ein Mitgliederwert mit einer bereits erstellten Nachricht initialisiert wird, da der Bezeichner localizes
nur der Definition neuer Nachrichten dient.
PlayerOne<localizes> : message = "Player One"
# The winning player's name:
PlayerName : message = PlayerOne
Attribute
Attribute in Verse beschreiben ein Verhalten, das außerhalb der Verse-Sprache verwendet wird (im Gegensatz zu Bezeichnern, die die Semantik von Verse beschreiben). Attribute können in der Codezeile vor den Definitionen hinzugefügt werden.
Die Attributsyntax verwendet ein @
, gefolgt von dem Schlüsselwort.
Steuerungsablauf
Der Kontrollfluss ist die Reihenfolge, in der ein Computer Anweisungen ausführt. In Verse gibt es verschiedene Ausdrücke, mit denen du den Ablauf in deinem Programm steuern kannst.
Block
Da Verse eine Kennung vor einem Codeblock erfordert, werden Codeblöcke mit Hilfe von block
-Ausdrücken verschachtelt. Ein verschachtelter Codeblock verhält sich ähnlich wie ein Codeblock. Wie bei Codeblöcken führt ein block
-Ausdruck einen neuen verschachtelten Bereich ein.
Block |
---|
Ergebnis: Letzter Ausdruck im |
Weiter Informationen findest du unter Block.
If
Mit dem if
-Ausdruck kannst du Entscheidungen treffen, die den Ablauf des Programms verändern. Wie in anderen Programmiersprachen unterstützt der if
-Ausdruck in Verse die bedingte Ausführung, aber in Verse verwenden die Bedingungen Erfolg und Fehlschlag, um die Entscheidung zu treffen.
If | if ... Dann |
---|---|
Ergebnis: Der letzte Ausdruck im | Ergebnis: Der letzte Ausdruck im |
if ... else | if ... else if ... else |
---|
Im folgenden Beispiel ergibt der Codeblock des if
-Ausdrucks entweder false
, wenn IsLightOn?
erfolgreich ist, oder true
, wenn IsLightOn?
fehlschlägt. Das logic
-Ergebnis wird dann in NewLightState
gespeichert.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Ein nützlicher Fall für den if
-Ausdruck in Verse ist, dass man fehlbare Ausdrücke ausprobieren kann, und wenn sie fehlschlagen, werden die Aktionen zurückgesetzt, als ob sie nie passiert wären. Weitere Einzelheiten zu dieser Funktion findest du unter Spekulative Ausführung.
Weiter Informationen findest du unter If.
Fall
Mit case
-Ausdrücken kannst du den Ablauf eines Programms aus einer Liste von Auswahlmöglichkeiten steuern. Die case-Anweisung in Verse ermöglicht es, einen Wert mit mehreren möglichen Werten zu vergleichen (als ob du = verwenden würdest) und den Code auf der Grundlage des übereinstimmenden Wertes auszuführen.
Behälter |
---|
Weiter Informationen findest du unter Case.
Loop
Mit dem loop
-Ausdruck werden die Ausdrücke im Schleifenblock für jede Iteration der Schleife wiederholt.
Schleife |
---|
Ergebnis: Das Ergebnis eines |
Im folgenden Beispiel erscheint und verschwindet eine Plattform alle ToggleDelay
Sekunden, solange das Spiel läuft.
loop:
Sleep(ToggleDelay) # Sleep(ToggleDelay) waits for ToggleDelay seconds before proceeding to the next instruction.
Platform.Hide()
Sleep(ToggleDelay)
Platform.Show() # The loop restarts immediately, calling Sleep(ToggleDelay) again.
Weiter Informationen findest du unter Schleife.
Schleifen stoppen
Ein Schleifenblock wird sich ewig wiederholen. Um die Schleife zu beenden, kannst du entweder die Schleife mit break
verlassen oder mit dem Rückgabeausdruck einer Funktion.
Schleife und Abbruch | Schleife und return |
---|---|
Ergebnis: Das Ergebnis eines | Ergebnis: Das Ergebnis eines |
Verschachtelte Schleife und break |
---|
Ergebnis: Das Ergebnis eines |
Weitere Informationen findest du unter Loop und Break.
„for“
Ein for
-Ausdruck, manchmal auch for-Schleife genannt, ist dasselbe wie ein Schleifenausdruck, außer dass for
-Ausdrücke einen Generator verwenden, um eine Folge von Werten nacheinander zu erzeugen und jedem Wert einen Namen zu geben.
Der Ausdruck for(Value : 1..3)
erzeugt zum Beispiel die Folge 1, 2, 3, und jede Zahl in der Folge erhält bei jeder Iteration den Namen Value
, so dass die for
-Schleife dreimal durchläuft.
Der for
-Ausdruck enthält zwei Teile:
Iterationsbezeichner: Die Ausdrücke in den Klammern. Der erste Ausdruck muss ein Generator sein, aber die anderen Ausdrücke können eine Konstanten-Initialisierung oder ein Filter sein.
Körper: Die Ausdrücke im Codeblock nach den Klammern.
für | for mit einem Filter |
---|---|
Ergebnis: Das Ergebnis eines | Ergebnis: Das Ergebnis eines |
for mit mehreren Generatoren | verschachtelte for-Blöcke |
---|---|
Ergebnis: Das Ergebnis eines | Ergebnis: Das Ergebnis eines |
Weiter Informationen findest du unter For.
Defer
Ein defer
-Ausdruck wird unmittelbar vor der Übertragung der Programmsteuerung außerhalb des Bereichs, in dem der defer
-Ausdruck erscheint, ausgeführt, einschließlich aller Ergebnisausdrücke, wie z. B. in einem return
. Es spielt keine Rolle, wie die Programmsteuerung übertragen wird.
defer | defer vor einem Abbruch |
---|---|
Ergebnis: Der | Ergebnis: Der |
Ein defer
-Ausdruck wird nicht ausgeführt, wenn er nach einem vorzeitigen Abbruch angetroffen wird.
defer mit vorzeitiger Rückgabe | defer mit einer abgebrochenen gleichzeitigen Aufgabe |
---|---|
Ergebnis: Der | Ergebnis: Der |
Mehrere defer
-Ausdrücke, die im selben Geltungsbereich vorkommen, kumulieren. Die Reihenfolge, in der sie ausgeführt werden, ist die umgekehrte Reihenfolge, in der sie angetroffen werden – First-In-Last-Out (FILO). Da das letzte defer
in einem bestimmten Bereich zuerst ausgeführt wird, können sich Ausdrücke innerhalb dieses letzten defer
auf einen Kontext beziehen, der von anderen defer
-Ausdrücken, die früher gefunden und später ausgeführt wurden, bereinigt wird.
Mehrere defer-Ausdrücke in einem Codeblock | Mehrere defer-Ausdrücke in verschiedenen Codeblöcken |
---|---|
Ergebnis: Der | Ergebnis: Der |
Zeitablauf und Gleichzeitigkeit
Zeitablaufsteuerung ist das Herzstück der Programmiersprache Verse, und dies wird mit gleichzeitigen Ausdrücken erreicht. Weitere Informationen zur Gleichzeitigkeit findest du unter Übersicht über die Gleichzeitigkeit.
Strukturierte Gleichzeitigkeit
Strukturierte Gleichzeitigkeit-Ausdrücke werden verwendet, um einen asynchronen logischen Zeitablauf zu spezifizieren und die blockierende Natur von asynchronen Ausdrücken mit einer Lebensdauer zu ändern, die auf einen bestimmten asynchronen Kontextbereich beschränkt ist (wie z. B. der Körper einer asynchronen Funktion).
Dies ähnelt einer strukturierten Flusskontrolle wie block
, if
, for
und loop
, die auf ihren zugehörigen Geltungsbereich beschränken.
sync | Zweig |
---|---|
Er führt alle Ausdrücke in seinem Codeblock gleichzeitig aus und wartet, bis sie alle beendet sind, bevor der nächste Ausdruck nach dem | Der Körper des |
Ergebnis: Das Ergebnis eines | Ergebnis: Ein |
race | rush |
---|---|
| Ähnlich wie | Ähnlich wie |
Ergebnis: Das Ergebnis eines | Ergebnis: Das Ergebnis eines |
Unstrukturierte Gleichzeitigkeit
Unstrukturierte Gleichzeitigkeitsausdrücke - von denen `Spawn` der einzige ist – haben eine Lebensdauer, die nicht auf einen bestimmten asynchronen Kontextbereich beschränkt ist und möglicherweise über den Bereich hinausgeht, in dem sie ausgeführt wurden.
Spawn |
---|
Der Körper eines |
Ergebnis: Ein |
Aufgabe
Eine Aufgabe ist ein Objekt, das verwendet wird, um den Zustand einer aktuell ausgeführten „async“-Funktion darzustellen. Aufgabenobjekte werden verwendet, um zu identifizieren, wo eine „async“-Funktion ausgesetzt wird und welche Werte die lokalen Variablen an diesem Aussetzungspunkt aufweisen.
# Get task to query / give commands to
# starts and continues independently
Task2 := spawn{Player.MoveTo(Target1)}
Task2.Await() # wait until MoveTo() completed
Module und Pfade
Ein Verse-Modul ist eine atomare Code-Einheit, die weiter verteilt werden kann, auf die du dich stützen kannst und die sich im Laufe der Zeit weiterentwickeln kann, ohne Abhängigkeiten zu unterbrechen. Du kannst ein Modul in deine Verse-Datei importieren, um Code-Definitionen aus anderen Verse-Dateien zu verwenden.
using: Um den Inhalt eines Verse-Moduls nutzen zu können, musst du das Modul über seinen Pfad importieren. | |
module: Abgesehen von Modulen, die durch Ordner in einem Projekt eingeführt werden, können Module innerhalb einer .verse -Datei mit dem Schlüsselwort |
Weitere Informationen findest du unter Module und Pfade.