Wat is het formele verschil in Scala tussen accolades en haakjes, en wanneer moeten ze worden gebruikt?

Wat is het formele verschil tussen het doorgeven van argumenten aan functies tussen haakjes ()en tussen accolades {}?

Het gevoel dat ik kreeg van het Programmeren in Scalaboek is dat Scala behoorlijk flexibel is en dat ik degene moet gebruiken die ik het leukst vind, maar ik merk dat sommige gevallen compileren en andere niet.

Bijvoorbeeld (alleen bedoeld als voorbeeld; ik zou elk antwoord op prijs stellen dat het algemene geval bespreekt, niet alleen dit specifieke voorbeeld):

val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )

=> fout: illegale start van eenvoudige uitdrukking

val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

=> prima.


Antwoord 1, autoriteit 100%

Ik heb ooit geprobeerd hierover te schrijven, maar heb het uiteindelijk opgegeven, omdat de regels nogal diffuus zijn. Kortom, je zult het onder de knie moeten krijgen.

Misschien is het het beste om je te concentreren op waar accolades en haakjes door elkaar kunnen worden gebruikt: bij het doorgeven van parameters aan methodeaanroepen. U maghaakjes vervangen door accolades als, en alleen als, de methode een enkele parameter verwacht. Bijvoorbeeld:

List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

Er is echter meer dat u moet weten om deze regels beter te begrijpen.

Verhoogde compilatiecontrole met haakjes

De auteurs van Spray raden ronde haakjes aan omdat ze een betere compilatiecontrole geven. Dit is vooral belangrijk voor DSL’s zoals Spray. Door tussen haakjes te gebruiken, vertel je de compiler dat deze maar één regel mag krijgen; daarom zal het klagen als u het per ongeluk twee of meer geeft. Nu is dit niet het geval met accolades – als je bijvoorbeeld ergens een operator vergeet, wordt je code gecompileerd en krijg je onverwachte resultaten en mogelijk een zeer moeilijk te vinden bug. Hieronder is gekunsteld (aangezien de uitdrukkingen puur zijn en op zijn minst een waarschuwing geven), maar maakt het punt:

method {
  1 +
  2
  3
}
method(
  1 +
  2
  3
)

De eerste compileert, de tweede geeft error: ')' expected but integer literal found. De auteur wilde 1 + 2 + 3schrijven.

Je zou kunnen stellen dat het vergelijkbaar is voor methoden met meerdere parameters met standaardargumenten; het is onmogelijk om per ongeluk een komma te vergeten om parameters te scheiden bij het gebruik van haakjes.

Breedzinnigheid

Een belangrijke, vaak over het hoofd geziene opmerking over breedsprakigheid. Het gebruik van accolades leidt onvermijdelijk tot uitgebreide code, aangezien de Scala-stijlgidsgeeft duidelijk aan dat het sluiten van accolades op hun eigen regel moet staan:

… de afsluitende accolade staat op zijn eigen regel direct na de laatste
regel van de functie.

Veel automatische herformatters, zoals in IntelliJ, voeren deze herformattering automatisch voor u uit. Probeer dus zoveel mogelijk ronde haakjes te gebruiken.

Infix-notatie

Als u infix-notatie gebruikt, zoals List(1,2,3) indexOf (2), kunt u haakjes weglaten als er maar één parameter is en deze schrijven als List(1, 2, 3) indexOf 2. Dit is niet het geval bij puntnotatie.

Merk ook op dat wanneer u een enkele parameter heeft die een multi-token-expressie is, zoals x + 2of a => a % 2 == 0, u moet haakjes gebruiken om de grenzen van de uitdrukking aan te geven.

Tuples

Omdat je soms haakjes kunt weglaten, heeft een tuple soms extra haakjes nodig, zoals in ((1, 2)), en soms kan het buitenste haakje worden weggelaten, zoals in (1, 2). Dit kan voor verwarring zorgen.

Functie/Gedeeltelijke Functie-letterwoorden met case

Scala heeft een syntaxis voor letterlijke functies en gedeeltelijke functiewaarden. Het ziet er zo uit:

{
    case pattern if guard => statements
    case pattern => statements
}

De enige andere plaatsen waar u case-statements kunt gebruiken, zijn met de trefwoorden matchen catch:

object match {
    case pattern if guard => statements
    case pattern => statements
}
try {
    block
} catch {
    case pattern if guard => statements
    case pattern => statements
} finally {
    block
}

U kunt case-statements niet in een andere context gebruiken. Dus als je casewilt gebruiken, heb je accolades nodig. Als je je afvraagt ​​wat het onderscheid tussen een functie en een deelfunctie letterlijk maakt, is het antwoord: context. Als Scala een functie verwacht, krijg je een functie. Als het een deelfunctie verwacht, krijg je een deelfunctie. Als beide worden verwacht, geeft het een foutmelding over ambiguïteit.

Uitdrukkingen en blokken

Haakjes kunnen worden gebruikt om subexpressies te maken. Krullende accolades kunnen worden gebruikt om codeblokken te maken (dit is geeneen letterlijke functie, dus pas op dat u het niet als zodanig probeert te gebruiken). Een codeblok bestaat uit meerdere statements, die elk een importstatement, een declaratie of een expressie kunnen zijn. Het gaat als volgt:

{
    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 )

Dus als je declaraties, meerdere statements, een importof iets dergelijks nodig hebt, heb je accolades nodig. En omdat een uitdrukking een statement is, kunnen haakjes tussen accolades staan. Maar het interessante is dat codeblokken ookuitdrukkingen zijn, dus je kunt ze overal binneneen uitdrukking gebruiken:

( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1

Dus, aangezien uitdrukkingen uitspraken zijn en codeblokken uitdrukkingen zijn, is alles hieronder geldig:

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...

Waar ze niet uitwisselbaar zijn

In principe kunt u {}nergens anders vervangen door ()of vice versa. Bijvoorbeeld:

while (x < 10) { x += 1 }

Dit is geen methodeaanroep, dus je kunt het niet op een andere manier schrijven. Welnu, je kunt accolades binnende haakjes voor de conditionplaatsen, en ook haakjes binnende accolades gebruiken voor het codeblok:

while ({x < 10}) { (x += 1) }

Dus ik hoop dat dit helpt.


Antwoord 2, autoriteit 15%

Er zijn een aantal verschillende regels en gevolgtrekkingen: ten eerste leidt Scala de accolades af wanneer een parameter een functie is, b.v. in list.map(_ * 2)worden de accolades afgeleid, het is gewoon een kortere vorm van list.map({_ * 2}). Ten tweede kun je met Scala de haakjes in de laatste parameterlijst overslaan, als die parameterlijst één parameter heeft en het een functie is, dus list.foldLeft(0)(_ + _)kan worden geschreven als list.foldLeft(0) { _ + _ }(of list.foldLeft(0)({_ + _})als je extra expliciet wilt zijn).

Als je echter casetoevoegt, krijg je, zoals anderen al zeiden, een gedeeltelijke functie in plaats van een functie, en Scala zal de accolades voor gedeeltelijke functies niet afleiden, dus list.map(case x => x * 2)zal niet werken, maar zowel list.map({case x => 2 * 2})als list.map { case x => x * 2 }zal.


Antwoord 3, autoriteit 6%

De gemeenschap probeert het gebruik van accolades en haakjes te standaardiseren, zie Scala Style Guide (pagina 21): http://www.codecommit.com/scala-style-guide.pdf

De aanbevolen syntaxis voor het aanroepen van methoden van hogere orde is om altijd accolades te gebruiken en de punt over te slaan:

val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }

Voor “normale” methode-aanroepen moet u de punt en haakjes gebruiken.

val result = myInstance.foo(5, "Hello")

Antwoord 4, autoriteit 5%

Ik denk niet dat er iets bijzonders of ingewikkelds is aan accolades in Scala. Houd een paar simpele dingen in gedachten om het schijnbaar complexe gebruik ervan in Scala onder de knie te krijgen:

  1. accolades vormen een codeblok, dat evalueert tot de laatste regel code (bijna alle talen doen dit)
  2. desgewenst kan een functie worden gegenereerd met het codeblok (volgt regel 1)
  3. accolades kunnen worden weggelaten voor code van één regel, behalve voor een hoofdletterclausule (Scala-keuze)
  4. haakjes kunnen worden weggelaten in functie-aanroep met codeblok als parameter (Scala-keuze)

Laten we een paar voorbeelden uitleggen aan de hand van de bovenstaande drie regels:

val tupleList = List[(String, String)]()
// doesn't compile, violates case clause requirement
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 ) 
// block of code as a partial function and parentheses omission,
// i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 })
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
// curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_)
// parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft{_+_}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1>
// curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0)(_ + _)
// parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0){_ + _}
// block of code and parentheses omission
List(1, 2, 3).foldLeft {0} {_ + _}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).foldLeft(0) _ + _
// error: ';' expected but integer literal found.
List(1, 2, 3).foldLeft 0 (_ + _)
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
// block of code that just evaluates to a value of a function, and parentheses omission
// i.e. foo({ println("Hey"); x => println(x) })
foo { println("Hey"); x => println(x) }
// parentheses omission, i.e. f({x})
def f(x: Int): Int = f {x}
// error: missing arguments for method f
def f(x: Int): Int = f x

5, Autoriteit 2%

Omdat u casegebruikt, definieert u een gedeeltelijke functie en gedeeltelijke functies vereisen krullende beugels.


Antwoord 6

Haakjes in een ideale coderingsstijl worden in principe gebruikt voor code met één regel.
Maar als het specifieke stuk code uit meerdere regels bestaat, is het gebruik van accolades een betere manier.


Antwoord 7

Met accolades krijgt u een puntkomma voor u en haakjes niet. Overweeg de functie takeWhile, aangezien deze een gedeeltelijke functie verwacht, alleen {case xxx => ??? }is een geldige definitie in plaats van haakjes rond hoofdlettergebruik.

Other episodes