Ich habe einmal versucht, darüber zu schreiben, aber ich habe am Ende aufgegeben, da die Regeln etwas diffus sind. Grundsätzlich müssen Sie den Dreh raus bekommen.
Vielleicht ist es am besten, sich darauf zu konzentrieren, wo geschweifte Klammern und Klammern austauschbar verwendet werden können: wenn Parameter an Methodenaufrufe übergeben werden. Sie können Klammern nur dann durch geschweifte Klammern ersetzen, wenn die Methode einen einzelnen Parameter erwartet. Zum Beispiel:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Sie müssen jedoch noch mehr wissen, um diese Regeln besser verstehen zu können.
Verbesserte Kompilierungsprüfung mit Parens
Die Autoren von Spray empfehlen runde Parens, da sie die Kompilierungsprüfung verbessern. Dies ist besonders wichtig für DSLs wie Spray. Durch die Verwendung von Parens teilen Sie dem Compiler mit, dass ihm nur eine einzige Zeile zugewiesen werden soll. Wenn Sie es versehentlich zwei oder mehr geben, wird es sich beschweren. Dies ist bei geschweiften Klammern nicht der Fall. Wenn Sie beispielsweise irgendwo einen Operator vergessen, wird Ihr Code kompiliert und Sie erhalten unerwartete Ergebnisse und möglicherweise einen sehr schwer zu findenden Fehler. Das Folgende ist erfunden (da die Ausdrücke rein sind und zumindest eine Warnung geben), macht aber den Punkt:
method {
1 +
2
3
}
method(
1 +
2
3
)
Der erste kompiliert, der zweite gibt error: ')' expected but integer literal found
. Der Autor wollte schreiben 1 + 2 + 3
.
Man könnte argumentieren, dass es für Multi-Parameter-Methoden mit Standardargumenten ähnlich ist; Es ist unmöglich, versehentlich ein Komma zu vergessen, um Parameter zu trennen, wenn Parens verwendet werden.
Ausführlichkeit
Eine wichtige, oft übersehene Anmerkung zur Ausführlichkeit. Die Verwendung von geschweiften Klammern führt unweigerlich zu ausführlichem Code, da im Scala-Styleguide eindeutig angegeben ist, dass das Schließen von geschweiften Klammern in einer eigenen Zeile erfolgen muss:
… Die schließende Klammer befindet sich in einer eigenen Zeile unmittelbar nach der letzten Zeile der Funktion.
Viele automatische Neuformatierer, wie in IntelliJ, führen diese Neuformatierung automatisch für Sie durch. Versuchen Sie also, runde Parens zu verwenden, wenn Sie können.
Infix-Notation
Wenn Sie die Infix-Notation verwenden, List(1,2,3) indexOf (2)
können Sie beispielsweise Klammern weglassen, wenn nur ein Parameter vorhanden ist, und ihn als schreiben List(1, 2, 3) indexOf 2
. Dies ist bei der Punktnotation nicht der Fall.
Beachten Sie auch, dass Sie, wenn Sie einen einzelnen Parameter haben, der ein Ausdruck mit mehreren Token ist, wie x + 2
oder a => a % 2 == 0
, Klammern verwenden müssen, um die Grenzen des Ausdrucks anzugeben.
Tupel
Da Sie manchmal Klammern weglassen können, benötigt ein Tupel manchmal zusätzliche Klammern wie in ((1, 2))
, und manchmal kann die äußere Klammer wie in weggelassen werden (1, 2)
. Dies kann zu Verwirrung führen.
Funktions- / Teilfunktionsliterale mit case
Scala hat eine Syntax für Funktions- und Teilfunktionsliterale. Es sieht aus wie das:
{
case pattern if guard => statements
case pattern => statements
}
Die einzigen anderen Stellen, an denen Sie case
Anweisungen verwenden können, sind die Schlüsselwörter match
und catch
:
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
Sie können keine case
Anweisungen in einem anderen Kontext verwenden . Wenn Sie also verwenden möchten case
, benötigen Sie geschweifte Klammern. Wenn Sie sich fragen, was die Unterscheidung zwischen einer Funktion und einer Teilfunktion wörtlich macht, lautet die Antwort: Kontext. Wenn Scala eine Funktion erwartet, erhalten Sie eine Funktion. Wenn eine Teilfunktion erwartet wird, erhalten Sie eine Teilfunktion. Wenn beide erwartet werden, gibt es einen Fehler bezüglich der Mehrdeutigkeit.
Ausdrücke und Blöcke
Klammern können verwendet werden, um Unterausdrücke zu machen. Geschweifte Klammern können verwendet werden, um Codeblöcke zu erstellen (dies ist kein Funktionsliteral, also hüten Sie sich davor, es wie eines zu verwenden). Ein Codeblock besteht aus mehreren Anweisungen, von denen jede eine Importanweisung, eine Deklaration oder ein Ausdruck sein kann. Es geht so:
{
import stuff._
statement ; // ; optional at the end of the line
statement ; statement // not optional here
var x = 0 // declaration
while (x < 10) { x += 1 } // stuff
(x % 5) + 1 // expression
}
( expression )
Wenn Sie also Deklarationen, mehrere Anweisungen import
oder ähnliches benötigen, benötigen Sie geschweifte Klammern. Und weil ein Ausdruck eine Aussage ist, können Klammern in geschweiften Klammern stehen. Das Interessante ist jedoch, dass Codeblöcke auch Ausdrücke sind, sodass Sie sie überall in einem Ausdruck verwenden können:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Da Ausdrücke Anweisungen und Codeblöcke Ausdrücke sind, gilt Folgendes:
1 // literal
(1) // expression
{1} // block of code
({1}) // expression with a block of code
{(1)} // block of code with an expression
({(1)}) // you get the drift...
Wo sie nicht austauschbar sind
Grundsätzlich kann man nicht ersetzen {}
mit ()
oder umgekehrt anderswo. Zum Beispiel:
while (x < 10) { x += 1 }
Dies ist kein Methodenaufruf, daher können Sie ihn nicht auf andere Weise schreiben. Nun, Sie können geschweifte Klammern in die Klammern für das setzen condition
sowie Klammern in die geschweiften Klammern für den Codeblock verwenden:
while ({x < 10}) { (x += 1) }
Ich hoffe, das hilft.
{}
- alles ein einziger reiner Ausdruck sein sollteList{1, 2, 3}.reduceLeft(_ + _)
es ungültig ist, meinen Sie damit, dass die Syntax fehlerhaft ist? Aber ich finde, dass Code kompiliert werden kann. Ich habe meinen Code hierList(1, 2, 3)
in allen Beispielen anstelle von verwendetList{1, 2, 3}
. Leider schlägt dies in Scalas aktueller Version (2.13) mit einer anderen Fehlermeldung (unerwartetes Komma) fehl. Sie müssten wahrscheinlich zu 2.7 oder 2.8 zurückkehren, um den ursprünglichen Fehler zu erhalten.Hier gibt es ein paar verschiedene Regeln und Schlussfolgerungen: Zunächst leitet Scala die Klammern ab, wenn ein Parameter eine Funktion ist, z. B. wenn
list.map(_ * 2)
die Klammern abgeleitet werden, ist es nur eine kürzere Form vonlist.map({_ * 2})
. Zweitens können Sie mit Scala die Klammern in der letzten Parameterliste überspringen, wenn diese Parameterliste einen Parameter enthält und eine Funktion ist,list.foldLeft(0)(_ + _)
die als geschrieben werden kannlist.foldLeft(0) { _ + _ }
(oderlist.foldLeft(0)({_ + _})
wenn Sie besonders explizit sein möchten).Wenn Sie jedoch hinzufügen, erhalten
case
Sie, wie andere bereits erwähnt haben, eine Teilfunktion anstelle einer Funktion, und Scala leitet die Klammern für Teilfunktionen nicht ab, funktioniert alsolist.map(case x => x * 2)
nicht, sondern beideslist.map({case x => 2 * 2})
undlist.map { case x => x * 2 }
wird.quelle
list.foldLeft{0}{_+_}
funktioniert.Die Community bemüht sich, die Verwendung von Klammern und Klammern zu standardisieren, siehe Scala Style Guide (Seite 21): http://www.codecommit.com/scala-style-guide.pdf
Die empfohlene Syntax für Methodenaufrufe höherer Ordnung besteht darin, immer geschweifte Klammern zu verwenden und den Punkt zu überspringen:
Für "normale" Metod-Aufrufe sollten Sie den Punkt und die Klammern verwenden.
quelle
+
,--
), NICHT regelmäßige Methoden wietakeWhile
. Der gesamte Punkt der Infix-Notation besteht darin, DSLs und benutzerdefinierte Operatoren zuzulassen. Daher sollte man sie in diesem Zusammenhang nicht immer verwenden.Ich glaube nicht, dass geschweifte Zahnspangen in Scala etwas Besonderes oder Komplexes haben. Um die scheinbar komplexe Verwendung in Scala zu beherrschen, sollten Sie nur ein paar einfache Dinge beachten:
Lassen Sie uns ein paar Beispiele anhand der oben genannten drei Regeln erklären:
quelle
{}
Verhalten bereitstellt . Ich habe den Wortlaut aus Gründen der Genauigkeit aktualisiert. Und für 4 ist es ein bisschen schwierig aufgrund der Interaktion zwischen()
und{}
, wie esdef f(x: Int): Int = f {x}
funktioniert, und deshalb hatte ich die 5 .. :)fun f(x) = f x
in SML gültig.f {x}
alsf({x})
scheint eine bessere Erklärung für mich zu sein, da das Denken()
und{}
Austauschen weniger intuitiv ist. Übrigens wird dief({x})
Interpretation etwas durch Scala spec (Abschnitt 6.6) gestützt:ArgumentExprs ::= ‘(’ [Exprs] ‘)’ | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ’)’ | [nl] BlockExp
Ich denke, es lohnt sich, ihre Verwendung in Funktionsaufrufen zu erklären und zu erklären, warum verschiedene Dinge passieren. Wie bereits erwähnt, definieren geschweifte Klammern einen Codeblock, der auch ein Ausdruck ist. Er kann also dort platziert werden, wo ein Ausdruck erwartet wird, und er wird ausgewertet. Bei der Auswertung werden die Anweisungen ausgeführt, und der Anweisungswert von last ist das Ergebnis der gesamten Blockbewertung (ähnlich wie in Ruby).
Damit können wir Dinge tun wie:
Das letzte Beispiel ist nur ein Funktionsaufruf mit drei Parametern, von denen jeder zuerst ausgewertet wird.
Um zu sehen, wie es mit Funktionsaufrufen funktioniert, definieren wir eine einfache Funktion, die eine andere Funktion als Parameter verwendet.
Um es aufzurufen, müssen wir eine Funktion übergeben, die einen Parameter vom Typ Int annimmt, damit wir das Funktionsliteral verwenden und an foo übergeben können:
Wie bereits erwähnt, können wir anstelle eines Ausdrucks einen Codeblock verwenden. Verwenden wir ihn also
Was hier passiert, ist, dass Code in {} ausgewertet wird und der Funktionswert als Wert der Blockauswertung zurückgegeben wird. Dieser Wert wird dann an foo übergeben. Dies ist semantisch dasselbe wie beim vorherigen Aufruf.
Aber wir können noch etwas hinzufügen:
Jetzt enthält unser Codeblock zwei Anweisungen, und da er ausgewertet wird, bevor foo ausgeführt wird, wird zuerst "Hey" gedruckt, dann wird unsere Funktion an foo übergeben, "foo eingeben" wird gedruckt und zuletzt "4" wird gedruckt .
Das sieht allerdings etwas hässlich aus und Scala lässt uns in diesem Fall die Klammern überspringen, damit wir schreiben können:
oder
Das sieht viel schöner aus und entspricht den ersteren. Hier wird noch ein Codeblock zuerst ausgewertet und das Ergebnis der Auswertung (x => println (x)) als Argument an foo übergeben.
quelle
foo({ x => println(x) })
. Vielleicht bin ich zu fest in meinen Wegen ...Da Sie verwenden
case
, definieren Sie eine Teilfunktion und Teilfunktionen erfordern geschweifte Klammern.quelle
Verbesserte Kompilierungsprüfung mit Parens
Die Autoren von Spray empfehlen, dass runde Parens eine verstärkte Kompilierungsprüfung durchführen. Dies ist besonders wichtig für DSLs wie Spray. Durch die Verwendung von Parens teilen Sie dem Compiler mit, dass ihm nur eine einzige Zeile zugewiesen werden soll. Wenn Sie ihm also versehentlich zwei oder mehr Zeilen gegeben haben, wird er sich beschweren. Dies ist bei geschweiften Klammern nicht der Fall. Wenn Sie beispielsweise einen Operator vergessen, an dem Ihr Code kompiliert wird, erhalten Sie unerwartete Ergebnisse und möglicherweise einen sehr schwer zu findenden Fehler. Unten ist erfunden (da die Ausdrücke rein sind und zumindest eine Warnung geben), macht aber den Punkt
Das erste kompiliert, das zweite gibt
error: ')' expected but integer literal found.
der Autor schreiben wollte1 + 2 + 3
.Man könnte argumentieren, dass es für Multi-Parameter-Methoden mit Standardargumenten ähnlich ist; Es ist unmöglich, versehentlich ein Komma zu vergessen, um Parameter zu trennen, wenn Parens verwendet werden.
Ausführlichkeit
Eine wichtige, oft übersehene Anmerkung zur Ausführlichkeit. Die Verwendung von geschweiften Klammern führt unweigerlich zu ausführlichem Code, da im Scala-Styleguide eindeutig angegeben ist, dass das Schließen von geschweiften Klammern in einer eigenen Zeile erfolgen muss: http://docs.scala-lang.org/style/declarations.html "... die schließende Klammer steht in einer eigenen Zeile unmittelbar nach der letzten Zeile der Funktion. " Viele automatische Neuformatierer, wie in Intellij, führen diese Neuformatierung automatisch für Sie durch. Versuchen Sie also, runde Parens zu verwenden, wenn Sie können. ZB
List(1, 2, 3).reduceLeft{_ + _}
wird:quelle
Bei geschweiften Klammern wurde ein Semikolon für Sie induziert und bei Klammern nicht. Betrachten Sie die
takeWhile
Funktion, da sie eine Teilfunktion erwartet, nur{case xxx => ??? }
eine gültige Definition anstelle von Klammern um den Fallausdruck.quelle