Fandom

Mini-Wikia

Fantom-DE/Ausdrücke

< Fantom-DE

1.771Seiten in
diesem Wiki
Seite hinzufügen
Diskussion0 Teilen

Störung durch Adblocker erkannt!


Wikia ist eine gebührenfreie Seite, die sich durch Werbung finanziert. Benutzer, die Adblocker einsetzen, haben eine modifizierte Ansicht der Seite.

Wikia ist nicht verfügbar, wenn du weitere Modifikationen in dem Adblocker-Programm gemacht hast. Wenn du sie entfernst, dann wird die Seite ohne Probleme geladen.

Mit Operatoren Ausdrücke bilden

Diese Seite ist im wesentlichen Teilen eine Übersetzung von http://fantom.org/doc/docLang/Expressions.html.

Operator-Präzedenz

Die Syntax für Ausdrücke ist bei Fantom ähnlich wie bei C, Java, C# usw. Hier ist eine Liste der möglichen Operationen in der Reihenfolge ihrer Präzedenz:

  • Primäre: (x) x.y x.y() x->y() x?.y x?.y() x?->y() x[y]
  • Unäre: ++x --x x++ x--  !x +x -x (T)x &x
  • Multiplikative: * /  %
  • Additive: + -
  • Intervallbildend: .. ..<
  • Relational: < <= >= > <=> is isnot as
  • Gleichheit: ==  != ===  !==
  • Konditionales Und: &&
  • Konditionales Oder: ||
  • Wenn-Ausdruck: x?t:f x?:y
  • Zuweisung: = *= /=  %= += -=
  • Zur Kollektion hinzufügen: ,

Operatormethoden

Fantom ist eine rein objektorientierte Sprache, in der alles ein Objekt ist und in der man auch an Value-Typen wie Bool und Int Methoden aufrufen kann. Aus diesem Grund sind fast alle Operatoren in Wirklichkeit Methodenaufrufe:

 a + b     =>  a.plus(b)
 a - b     =>  a.minus(b)
 a * b     =>  a.mult(b)
 a / b     =>  a.div(b)
 a % b     =>  a.mod(b)
 a[b]      =>  a.get(b)
 a[b] = c  =>  a.set(b, c)
 -a        =>  a.negate()
 ++a, a++  =>  a = a.increment()
 --a, a--  =>  a = a.decrement()
 a == b    =>  a.equals(b)
 a != b    =>  !a.equals(b)
 a <=> b   =>  a.compare(b)
 a > b     =>  a.compare(b) > 0
 a >= b    =>  a.compare(b) >= 0
 a < b     =>  a.compare(b) < 0
 a <= b    =>  a.compare(b) <= 0
 a,b,c     =>  it.add(a).add(b).add(c) (in It-Blöcken)

Angenommen, wir haben zwei Variablen a und b, die beide vom Typ Int sind. Dann ist der Ausdruck a+b im Grunde nichts weiter als Syntax-Zucker für einen Aufruf der Methode sys::Int.plus wie a.plus(b). In der Fantom-Dokumentation finden Sie weitere Einzelheiten dazu unter Methoden→Operatoren.

Prefix- und Postfix-Operatoren

Die Operatoren ++ und -- können wie bei den Sprachen der C-Familie vor oder hinter dem Operanden angeordnet sein. Beide Operatoren weisen das Ergebnis des Aufrufs von increment oder decrement der Operanden-Variablen zu. Wenn der Operator vorangestellt ist, entspricht das Ergebnis des Ausdrucks dieser Zuweisung. Andernfalls entspricht es dem Wert des Operanden vor der Zuweisung.

Gleichheitsoperatoren

Die Gleichheitsoperatoren == und != nutzen beide die virtuelle Methode sys::Obj.equals. In den meisten Typen ist diese Methode derart überschrieben, dass jeweils der Inhalt des Objekts verglichen wird. Wenn equals nicht überschrieben ist, dann wird standardmäßig die Gleichheit der Referenz (also die Identität) geprüft.

Vergleichsperatoren

Die relationalen Operatoren wie < und > greifen alle auf die virtuelle Methode sys::Obj.compare zu. Viele Typen, bei denen das Ordnen einen Sinn gibt, überschreiben diese Methode, so dass sie -1, 0 oder 1 zurück gibt. Wenn compare nicht überschrieben ist, vergleicht die Standardimplementierung das Ergebnis des Aufrufs der Methode toStr beim Operanden.

Der Compiler übersetzt den numerischen Rückgabewert in Abhängigkeit von dem verwendeten Operator in einen Booleschen Wert. Der spezielle Operator <=> gibt den Int-Wert von -1, 0, 1 direkt zurück. Meistens verwendet man den "Raumschiff-Operator" <=> für benutzerdefinierte Sortierungen mit Hilf einder Closure:

people.sort |Person a, Person b->Int| { return a.age <=> b.age }

Machen Sie sich keine Sorgen, wenn dieses Programmstück für sie nicht recht verständlich ist. Lesen Sie einfach weiter, bis wir zu den Closures kommen.

Vergleich mit Null

Die Gleichheits- und Vergleichsoperatoren gehen mit Situationen, in denen einer der Operanden null ist, in einer Weise um, dass niemals eine NullErr-Exception ausgelöst wird. Ein Nicht-Null-Wert und ein Null-Wert sind niemals gleich, zwei Null-Werte sind dagegen immer gleich. Was die Vergelichsoperatoren angeht, ist null immer kleiner als eine Nicht-Null-Wert. Diese spezielle Behandlung wird nicht angewendet, wenn man die Methoden equals oder compare in einem normalen Methodenaufruf verwendet. Auch bei anderen Funktionsoperatoren gibt es keine solche Spezialbehandlung.

Identitätsoperatoren

Die Identitätsoperatoren === und !== werden auch als same- und not same-Operatoren bezeichnet. Sie dienen zur Prüfung, ob zwei Variablen dasselbe Objekt im Arbeitsspeicher referenzieren. Anders als die Funktionsoperatoren == und != führen sie nicht zum einem Aufruf der Methode equals. Mit Werttypen (Bool, Int, Float können sie nicht verwendet werden.

Bedingungsoperatoren

Die Bedingungsoperatoren !, && und || werden mit Booleschen Ausdrücken verwendet. Mit && können Sie ein logisches Und, mit || ein logisches Oder ausführen. Beide Operatoren schließen kurz, das heißt sie lassen die zweite Prüfung weg, wenn die erste bereits ein endgültiges Ergebnis liefert (false für && und true für ||). Der Operator ! führt eine logische Negation aus.

Hier sind einige Code-Beispiele für Bedingungsoperatoren:

t := true
f := false
t && f  => ergibt false
t && t  => ergibt true
f || t  => ergibt true
!t      => ergibt false

Der ternäre Operator

Der ternäre Operator verbindet drei Ausdrücke und bietet eine bequeme Möglichkeit, einen Wert in Abhängigkeit von eine Wenn-Oder-Bedingung zuzuweisen:

condExpr ? trueExpr : falseExpr

Der Ausdruck condExpr muss ein Boolesches Ergebnis haben. Wenn condExpr den Wert true hat, dann ist das Ergebnis des gesamten Ausdrucks trueExpr, andernfalls ist es falseExpr. Einige Beispiele:

3 > 4 ? "ja" : "nein"  => evaluates to "nein"
6 > 4 ? "ja" : "nein"  => evaluates to "ja"

In Fantom kann eines der beiden Ergebnisse einer ternären Operation auch eine throw-Anweisung sein:

val := isValid(key) ? map[key] : throw ArgErr("ungültiger Schlüssel")

Operatoren für den Umgang mit Null

Fantom hat einige Operatoren aus Groovy übernommen, die das Arbeiten mit null etwas angenehmer macht:

  • Elvis-Operator: x ?: y (seitwärts betrachten wie ein Smiley)
  • Sicherer Aufruf: x?.y
  • Sicherer dynamischer Aufruf: x?->y

Der Elvis-Operator

Der Elvis-Operator wertet den Ausdruck auf der linken Seite aus. Ist dieser nicht null, dann ist dies zugelich des Ergebnis des gesamten Ausdruck. Wenn er jedoch null ist, dann ist die rechte Seite das Ergebnis des gesamten Ausdrucks. Wenn die linke Seite nicht null ergibt, wird die rechte Seite kurzgeschlossen. Das sieht ungefähr so aus:

// schwerer Weg
file != null ? file : defaultFile

// leichter Weg
file ?: defaultFile

Der Elvis-Operator kann nicht mit einem nicht-nullbaren Typ verwendet werden, denn dieser kann definitionsgemäßt nie null sein.

Wie der ternäre Operator kann auch der Elvis-Operator eine throw-Anweisung auf der rechten Seite des Ausdrucks haben:

val := map[key] ?: throw ArgErr("Schlüssel nicht gefunden")

Sicherer Aufruf

Die Operatoren für sicheren Aufruf dienen dazu, Methodenaufrufe und Feldzugriffe kurzzuschließen, wenn das Ziel null ist. Das Ergebnis des Kurzschließens ist, dass der Wert des gesamten Ausdrucks null ergibt. Dies ist recht nützlich, um eine Reihe von Werten in im laufe eines verketteten Aufrufs auf null zu prüfen:

// schwerer Weg
Str? email := null
if (userList != null)
{
  user := userList.findUser("bob")
  if (user != null) email = user.email
}

// leichter Weg
email := userList?.findUser("bob")?.email
Wenn wir an irgendeiner Stelle einer nullgesicherten Aufrufkette auf null stoßen, dann wird der gesamte Ausdruck kurzgeschlossen und der Ausdruck hat das Ergebnis null. Den Operator ?-> als nullgesicherte Version des Operators für dynamischen Aufruf (->) verwenden.

Die Operatoren für sicheren Aufruf können nicht mit einem nicht-nullbaren Typ verwendet werden, da dieser definitionsgemäß immer null ist. Das Ergebnis eines sicheren Aufrufs ist immer nullbar:

x := str.size   =>  x hat den Typ Int
x := str?.size  =>  x hat den Typ Int?

Typprüfung und -umwandlung

Der Cast-Operator dient zur Durchführung von Typkonvertierungen. Die Syntax verwendet wie die C-Sprachen Klammern, z.B. (Int)x. Wenn ein Typcast zur Laufzeit scheitert, wird eine sys::CastErr-Exception ausgelöst.

Mit den Operatoren is, isnot und as kann man den Typ eines Objekts zur Laufzeit prüfen:

  • is liefert einen Bool-Wert, der besagt, ob der Operand den angegebenen Typ implementiert (wie der instanceof-Operator in Java). Wenn das Zielobjekt null ist, dann ergibt der Operator false.
  • isnot ist semantisch gleichbedeutend wie !(x is Type). Wenn das Zielobjekt null, dann ergibt der Operator true.
  • as Liefert ein Objekt, das auf den angegebenen Typ abgebildet ist, oder null, wenn es sich nicht um eine Instanz dieses Typs handelt (wie C#):
 Obj obj := 123
 obj is Str     =>  ergibt false
 obj is Num     =>  ergibt true
 obj isnot Str  =>  ergibt true
 obj isnot Num  =>  ergibt false
 obj as Float   =>  ergibt null
 obj as Int     =>  ergibt 6 (Ausdruck hat den Typ Int)

Die Nullbarkeit von Typen bleibt bei der Verwendung der Operatoren is, isnot und as unbeachtet. Beispielsweise werden die beiden folgenden Ausdrücke als gleichbedeutend angesehen:

obj is Str
obj is Str?

Der as-Operator liefert definitionsgemäß einen nullbaren Typ. Beispielsweise liefert der folgende Ausdruck Str? und nicht Str:

x := obj as Str  => x hat den Typ Str?

Indizierung

Abhängig von seiner Verwendung wird der Opertor [] auf drei verschiedene Methodenaufrufe abgebildet:

a[b]      =>  a.get(b)
a[b] = c  =>  a.set(b, c)
a[b]      =>  a.getRange(b) // wenn b eine Intervall ist

Typischerweise ist a[b] eine Abkürzung für den Aufruf a.get(b). Beispielsweise ermöglicht die Methode sys::List.get ein Listenelement anhand eines Int-Index auszulesen. Man kann die Abkürzung [] immer dann verwenden, wenn eine Klasse eine Operatormethode get hat, die mit der Facette @Operator annotiert ist.

Sehen sie sich folgendes Code-Stück an:

list := ["a", "b", "c"]
list.get(2)
list[2]
list.get("2")  // Fehler
list["2"]      // Fehler

Der Ausdruck list[2] bewirkt genau dasselbe wie list.get(2). Die letzten beiden Code-Zeilen führen zu Compiler-Fehlern, weil wir dort versuchen, einen Str zu übergeben, obwohl ein Int erwartet wird.

Wenn die Indizierung auf der linken Seite der Zuweisung verwendet wird, z.B. a[b] = c, dann wird der Index-Operator auf a.set(b, c) abgebildet. So haben verhalten sich beispielsweise die beiden folgenden Code-Zeilen genau gleich:

map.set("tmj", "Too Much Joy")
map["tmj"] = "Too Much Joy"

Wenn der []-Operator mit einem Index vom Typ sys::Range verwendet wird, dann bilden wir dies auf den Methodenaufruf a.getRange(b) ab und liefern einen Abschnitt der Liste oder des String. Hier sind einige Beispiele, die Teil-Strings erzeugen:

s := "abcd"
s[0..2]  => "abc"
s[3..3]  => "d"
s[0..<2] => "ab"

start := 0; end := 2
s[start..<end] => "ab"

Mit .. geben wir einen inklusiven und mit ..< eine exklusiven End-Index an. Beachten Sie auch, dass man mit den Intervall-Operatoren beliebige Ausdrücke verwenden und dadurch die Abschnittsbildung sehr kompakt formulieren kann.

Konventionsgemäß erlauben es die Fantom-APIs mit ganzzahliger Indizierung die Verwendung von negativen Zahlen, die vom Ende der Liste her zählen. So kann man beispielsweise mit -1 das letzte Element einer Liste (oder das letzte Zeichen eines String) indizieren. Negative Indizierung ist bei allen drei Formen des []-Operators möglich:

list := ["a", "b", "c", "d"]
list[-2]           =>  evaluates to "c"
list[-1] = "last"  =>  replaces list[3] with "last"
list[1..-1]        =>  evaluates to ["b", "c", "last"]

Die meisten Methoden von List und Str, die Indexwerte akzeptieren, funktionieren auch mit negativem Index.

Bit-Operatoren

Fantom kennt keine Operatoren für Operationen auf Bit-Ebene. Stattdessen muss man normale Methodenaufrufe verwenden:

~a      =>   a.not
a & b   =>   a.and(b)
a | b   =>   a.or(b)
a ^ b   =>   a.xor(b)
a << b  =>   a.shiftl(b)
a >> b  =>   a.shiftr(b)

Serialisierungsausdrücke

Fantom unterstützt drei Konstrukte für Ausdrücke, die den Zweck haben, die Programmiersprache zu einer echten Obermenge der Syntax für die Serialisierung zu machen:

  • Simples
  • It-Blöcke
  • Kollektionen

Simples

Ein Simple ist ein spezieller serialisierbarer Typ, der sich über eine String-Darstellung serialisieren lässt. Dafür gibt es in Fantom einen einfachen Ausdruck:

<type>(<str>)

// Beispiel:
Version("3.2")

// ist Syntax-Zucker für
Version.fromStr("3.2")

Damit ein solcher Ausdruck verwendet werden kann, muss der betreffende Typ über eine statische Methode fromStr verfügen, die einen Str-Parameter annimmtt und eine Instanz von sich selbst liefert. Die Methode kann zusätzliche Parameter haben, sofern diese mit Vorgabewerten ausgestattet sind. Der Typ muss nicht unbedingt die Facette @Serializable implementieren, um diesen Ausdruck verwenden zu können (zum Serialisieren ist dies allerdings erforderlich).

It-Blöcke

It-Blöcke dienen dazu, zusammengesetzte Ausdrücke zu schreiben und werden typischer Weise zu Initialisierung von Objekten verwendet. Hier ein Beispiel:

Address
{
  street = "123 Happy Lane"
  city   = "Houston"
  state  = "TX"
}

// ist Syntax-Zucker für
Address.make.with |Address it|
{
  it.street = "123 Happy Lane"
  it.city   = "Houston"
  it.state  = "TX"
}

Kollektionen

Mit einem It-Block kann man auch eine Kollektion initialisieren, sofern diese eine Methode add hat. Jeder Ausdruck innerhalb des It-Blocks, der mit einem Komma abgeschlossen ist, wird als ein Aufruf von it.add betrachtet:

a,       =>  it.add(a)
a, b     =>  it.add(a).add(b)
a, b, c  =>  it.add(a).add(b).add(c)

Beachten Sie, dass der Komma-Operator die Aufrufe an add weiterreicht, daher muss die add-Methode mit der Facette @Operator annotiert sein und den verkettbaren Typ This zurückgeben.

Hier ist ein Beispiel für FWT:

Menu
{
  text = "File"
  MenuItem { text = "Open"; onSelect=#open.func } },
  MenuItem { text = "Save"; onSelect=#save.func },
}

// is syntax sugar for (note: can't actually use it as param name)
Menu.make.with |Menu it|
{
  it.text = "File"
  it.add(MenuItem { text = "Open"; onSelect=#open.func })
    .add(MenuItem { text = "Save"; onSelect=#save.func })
}

Fortgeschrittene Operatoren

In Fantom gibt es einige weitere Operatoren, die an anderer Stelle behandelt werden:

  • Closures sind Ausdrücke, die innerhalb eines Methodenrumpfs einen neue Funktion erzeugen.
  • Der Funktionsaufruf-Operator () wird verwendet, um eine Funktionsvariable aufzurufen.
  • Der dynamische Aufrufoperator -> dient dazu, eine Methode ohne Kompilierzeit-Überprüfung aufzurufen.
  • Der Feldzugriffsoperator & wird dazu verwendet, unter Umgehung der Getter- und Setter-Methoden direkt auf ein Feld zuzugreifen.

Auch bei Fandom

Zufälliges Wiki