Próbowałem kiedyś o tym napisać, ale ostatecznie zrezygnowałem, ponieważ zasady są nieco rozproszone. Zasadniczo musisz się z tym pogodzić.
Być może najlepiej skoncentrować się na tym, gdzie nawiasy klamrowe i nawiasy mogą być używane zamiennie: podczas przekazywania parametrów do wywołań metod. Ty może zastąpić nawias z klamrami, wtedy i tylko wtedy, gdy metoda oczekuje jednego parametru. Na przykład:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Jednak musisz wiedzieć więcej, aby lepiej zrozumieć te zasady.
Zwiększone sprawdzanie kompilacji za pomocą parens
Autorzy Spray zalecają okrągłe pareny, ponieważ dają zwiększone sprawdzanie kompilacji. Jest to szczególnie ważne w przypadku DSL, takich jak spray. Za pomocą parens informujesz kompilator, że powinien mieć tylko jedną linię; dlatego jeśli przypadkowo podasz dwa lub więcej, będzie narzekać. Teraz nie jest tak w przypadku nawiasów klamrowych - jeśli na przykład zapomnisz gdzieś operatora, kod się skompiluje, a otrzymasz nieoczekiwane wyniki i potencjalnie bardzo trudny do znalezienia błąd. Poniżej wymyślono (ponieważ wyrażenia są czyste i przynajmniej dają ostrzeżenie), ale wskazuje na to:
method {
1 +
2
3
}
method(
1 +
2
3
)
Pierwsza kompiluje, druga daje error: ')' expected but integer literal found
. Autor chciał pisać 1 + 2 + 3
.
Można argumentować, że podobnie jest w przypadku metod wieloparametrowych z domyślnymi argumentami; nie można przypadkowo zapomnieć przecinka, aby oddzielić parametry podczas korzystania z parens.
Gadatliwość
Ważna często pomijana uwaga na temat gadatliwości. Używanie nawiasów klamrowych nieuchronnie prowadzi do pełnego kodu, ponieważ przewodnik po stylu Scala wyraźnie stwierdza, że zamykanie nawiasów klamrowych musi odbywać się w ich własnej linii:
… Nawias zamykający znajduje się w swoim własnym wierszu bezpośrednio po ostatnim wierszu funkcji.
Wiele auto-reformaterów, takich jak IntelliJ, automatycznie wykona to ponowne formatowanie. Staraj się więc trzymać okrągłe pareny, kiedy możesz.
Notacja Infix
Gdy używasz notacji infix, List(1,2,3) indexOf (2)
możesz pominąć nawias, jeśli jest tylko jeden parametr i zapisać go jako List(1, 2, 3) indexOf 2
. Nie dotyczy to notacji kropkowej.
Zauważ też, że jeśli masz pojedynczy parametr, który jest wyrażeniem wieloznacznym, takim jak x + 2
lub a => a % 2 == 0
, musisz użyć nawiasu, aby wskazać granice wyrażenia.
Krotki
Ponieważ czasami można pominąć nawias, czasami krotka wymaga dodatkowego nawiasu, np. W ((1, 2))
, a czasami nawias zewnętrzny można pominąć, jak w (1, 2)
. Może to powodować zamieszanie.
Literały funkcji / funkcji częściowych za pomocą case
Scala ma składnię dla literałów funkcji i częściowych funkcji. To wygląda tak:
{
case pattern if guard => statements
case pattern => statements
}
Jedynymi innymi miejscami, w których można używać case
instrukcji, są słowa kluczowe match
i catch
:
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
Nie można używać case
instrukcji w żadnym innym kontekście . Tak więc, jeśli chcesz użyć case
, potrzebujesz nawiasów klamrowych. Jeśli zastanawiasz się, co sprawia, że różnica między funkcją a funkcją częściową jest dosłowna, odpowiedź brzmi: kontekst. Jeśli Scala spodziewa się funkcji, otrzymasz ją. Jeśli oczekuje funkcji częściowej, otrzymujesz funkcję częściową. Jeśli spodziewane są oba, pojawia się błąd dotyczący niejednoznaczności.
Wyrażenia i bloki
Nawiasów można użyć do wykonania podwyrażeń. Nawiasów klamrowych można używać do tworzenia bloków kodu ( nie jest to dosłowna funkcja, więc wystrzegaj się próbowania używania go jak jednego). Blok kodu składa się z wielu instrukcji, z których każda może być instrukcją importu, deklaracją lub wyrażeniem. To wygląda tak:
{
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 )
Tak więc, jeśli potrzebujesz deklaracji, wielu instrukcji, import
lub czegoś podobnego, potrzebujesz nawiasów klamrowych. Ponieważ wyrażenie jest stwierdzeniem, nawiasy klamrowe mogą pojawiać się w nawiasach. Ale ciekawe jest to, że bloki kodu są również wyrażenia, więc można z nich korzystać w dowolnym miejscu wewnątrz wyrażenia:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Ponieważ wyrażenia są wyrażeniami, a bloki kodów są wyrażeniami, wszystko poniżej jest poprawne:
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...
Gdzie nie są wymienne
Zasadniczo, nie można zastąpić {}
z ()
lub odwrotnie nigdzie indziej. Na przykład:
while (x < 10) { x += 1 }
To nie jest wywołanie metody, więc nie możesz napisać go w żaden inny sposób. Cóż, można umieścić nawiasy klamrowe wewnątrz nawiasów dla osób condition
, jak również wykorzystania nawiasie wewnątrz nawiasy dla bloku kodu:
while ({x < 10}) { (x += 1) }
Mam nadzieję, że to pomoże.
{}
byłoby, gdyby żadna metoda nie była używana - wszystko powinno być pojedynczym czystym wyrażeniemList{1, 2, 3}.reduceLeft(_ + _)
jest nieprawidłowy, masz na myśli, że ma on błąd składniowy? Ale okazuje się, że ten kod można skompilować. Umieściłem tutajList(1, 2, 3)
we wszystkich przykładach zamiastList{1, 2, 3}
. Niestety, w bieżącej wersji Scali (2.13) nie powiedzie się to z innym komunikatem o błędzie (nieoczekiwany przecinek). Prawdopodobnie musiałbyś wrócić do wersji 2.7 lub 2.8.Dzieje się tu kilka różnych reguł i wniosków: po pierwsze, Scala podaje nawiasy klamrowe, gdy parametr jest funkcją, np. W
list.map(_ * 2)
nawiasach klamrowych wnioskuje się, że jest to po prostu krótsza formalist.map({_ * 2})
. Po drugie, Scala pozwala pominąć nawiasy na liście ostatnich parametrów, jeśli lista parametrów ma jeden parametr i jest funkcją, więclist.foldLeft(0)(_ + _)
można go zapisać jakolist.foldLeft(0) { _ + _ }
(lublist.foldLeft(0)({_ + _})
jeśli chcesz być bardziej jawny).Jeśli jednak dodać
case
można dostać, jak wspominają inni, częściowy funkcja zamiast funkcją i Scala nie będzie wywnioskować szelki dla funkcji częściowych, więclist.map(case x => x * 2)
nie będzie działać, ale zarównolist.map({case x => 2 * 2})
ilist.map { case x => x * 2 }
woli.źródło
list.foldLeft{0}{_+_}
działa.Społeczność stara się ustandaryzować użycie nawiasów klamrowych i nawiasów, patrz Przewodnik po stylu Scala (strona 21): http://www.codecommit.com/scala-style-guide.pdf
Zalecaną składnią dla wywołań metod wyższego rzędu jest zawsze używanie nawiasów klamrowych i pomijanie kropki:
Do „normalnych” wywołań metod należy używać kropki i nawiasów.
źródło
+
,--
), a nie zwykłych metod, takich jaktakeWhile
. Cały punkt notacji infix polega na umożliwieniu DSL i operatorom niestandardowym, dlatego nie należy go używać w tym kontekście przez cały czas.Nie sądzę, żeby w Scali było coś szczególnego lub złożonego w nawiasach klamrowych. Aby opanować pozornie skomplikowane użycie ich w Scali, pamiętaj o kilku prostych rzeczach:
Wyjaśnijmy kilka przykładów według powyższych trzech zasad:
źródło
{}
. Zaktualizowałem sformułowanie pod kątem precyzji. A dla 4 jest to trochę trudne ze względu na interakcję między()
i{}
, jakdef f(x: Int): Int = f {x}
działa, i dlatego miałem 5. pozycję. :)fun f(x) = f x
jest ważny w SML.f {x}
jakf({x})
wydaje się być lepszym wytłumaczeniem dla mnie, jak myślenie()
i{}
wymienne jest mniej intuicyjne. Nawiasem mówiąc,f({x})
interpretacja jest nieco poparta specyfikacją Scali (rozdział 6.6):ArgumentExprs ::= ‘(’ [Exprs] ‘)’ | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ’)’ | [nl] BlockExp
Myślę, że warto wyjaśnić ich użycie w wywołaniach funkcji i dlaczego zdarzają się różne rzeczy. Jak ktoś już powiedział, nawiasy klamrowe definiują blok kodu, który jest również wyrażeniem, więc można go umieścić tam, gdzie oczekuje się wyrażenia i zostanie ono ocenione. Podczas oceny wykonywane są jego instrukcje, a wartość instrukcji last jest wynikiem oceny całego bloku (podobnie jak w Ruby).
Dzięki temu możemy robić takie rzeczy jak:
Ostatni przykład to tylko wywołanie funkcji z trzema parametrami, z których każdy jest oceniany jako pierwszy.
Teraz, aby zobaczyć, jak to działa z wywołaniami funkcji, zdefiniujmy prostą funkcję, która przyjmuje inną funkcję jako parametr.
Aby go wywołać, musimy przekazać funkcję, która przyjmuje jeden parametr typu Int, abyśmy mogli użyć literału funkcji i przekazać go do foo:
Teraz, jak powiedziano wcześniej, możemy użyć bloku kodu zamiast wyrażenia, więc użyjmy go
To, co się tu dzieje, polega na tym, że kod wewnątrz {} jest analizowany, a wartość funkcji jest zwracana jako wartość oceny bloku, ta wartość jest następnie przekazywana do foo. Jest to semantycznie to samo co poprzednie wywołanie.
Ale możemy dodać coś więcej:
Teraz nasz blok kodu zawiera dwie instrukcje, a ponieważ jest on oceniany przed wykonaniem foo, dzieje się tak, że najpierw drukowane jest „Hej”, a następnie nasza funkcja jest przekazywana do foo, drukowane jest „Wprowadzanie foo”, a na końcu drukowane jest „4” .
Wygląda to nieco brzydko, a Scala pozwala nam pominąć nawias w tym przypadku, abyśmy mogli napisać:
lub
Wygląda to o wiele ładniej i jest odpowiednikiem poprzednich. Tutaj nadal blok kodu jest oceniany jako pierwszy, a wynik oceny (którym jest x => println (x)) jest przekazywany jako argument do foo.
źródło
foo({ x => println(x) })
. Może jestem zbytPonieważ używasz
case
, definiujesz funkcję częściową, a funkcje częściowe wymagają nawiasów klamrowych.źródło
Zwiększone sprawdzanie kompilacji za pomocą parens
Autorzy Spraya zalecają, aby okrągłe pareny zwiększały sprawdzanie kompilacji. Jest to szczególnie ważne w przypadku DSL, takich jak spray. Używając parens, mówisz kompilatorowi, że powinien otrzymać tylko jedną linię, dlatego jeśli przypadkowo dasz mu dwa lub więcej, będzie narzekać. Teraz nie jest tak w przypadku nawiasów klamrowych, jeśli na przykład zapomnisz operatora gdzieś, który skompiluje Twój kod, otrzymasz nieoczekiwane wyniki i potencjalnie bardzo trudny do znalezienia błąd. Poniżej jest wymyślone (ponieważ wyrażenia są czyste i przynajmniej dają ostrzeżenie), ale ma sens
Pierwsza kompilacja, druga daje
error: ')' expected but integer literal found.
autorowi chęć napisania1 + 2 + 3
.Można argumentować, że podobnie jest w przypadku metod wieloparametrowych z domyślnymi argumentami; nie można przypadkowo zapomnieć przecinka, aby oddzielić parametry podczas korzystania z parens.
Gadatliwość
Ważna często pomijana uwaga na temat gadatliwości. Używanie nawiasów klamrowych nieuchronnie prowadzi do pełnego kodu, ponieważ przewodnik po stylu Scala wyraźnie stwierdza, że zamykanie nawiasów klamrowych musi odbywać się w ich własnej linii: http://docs.scala-lang.org/style/declarations.html "... nawias klamrowy znajduje się we własnej linii bezpośrednio po ostatnim wierszu funkcji. ” Wiele auto-reformaterów, takich jak Intellij, automatycznie wykona to ponowne formatowanie. Staraj się więc trzymać okrągłe pareny, kiedy możesz. Np.
List(1, 2, 3).reduceLeft{_ + _}
Staje się:źródło
W przypadku nawiasów klamrowych wywołano dla Ciebie średnik, a nawiasów nie. Rozważ
takeWhile
funkcję, ponieważ oczekuje ona funkcji częściowej,{case xxx => ??? }
jest to tylko poprawna definicja zamiast nawiasów wokół wyrażenia wielkości liter.źródło