Scala 中大括号和圆括号之间的正式区别是什么,何时应该使用它们?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4386127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the formal difference in Scala between braces and parentheses, and when should they be used?
提问by Jean-Philippe Pellet
What is the formal difference between passing arguments to functions in parentheses ()and in braces {}?
将参数传递给括号中()和大括号中的函数之间的正式区别是什么{}?
The feeling I got from the Programming in Scalabook is that Scala's pretty flexible and I should use the one I like best, but I find that some cases compile while others don't.
我从《Scala 编程》一书中得到的感觉是 Scala 非常灵活,我应该使用我最喜欢的那个,但我发现有些情况可以编译,而另一些则不能。
For instance (just meant as an example; I would appreciate any response that discusses the general case, not this particular example only):
例如(仅作为示例;我将不胜感激任何讨论一般情况的回复,而不仅仅是这个特定示例):
val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
=> error: illegal start of simple expression
=> 错误:简单表达式的非法开始
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
=> fine.
=> 很好。
回答by Daniel C. Sobral
I tried once to write about this, but I gave up in the end, as the rules are somewhat diffuse. Basically, you'll have to get the hang of it.
我曾经尝试过写这个,但最后我放弃了,因为规则有点分散。基本上,你必须掌握它的窍门。
Perhaps it is best to concentrate on where curly braces and parenthesis can be use interchangeably: when passing parameters to method calls. You mayreplace parenthesis with curly braces if, and only if, the method expects a single parameter. For example:
也许最好将注意力集中在花括号和圆括号可以互换使用的地方:在将参数传递给方法调用时。当且仅当该方法需要单个参数时,您可以用花括号替换括号。例如:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
However, there's more you need to know to better grasp these rules.
但是,您需要了解更多信息才能更好地掌握这些规则。
Increased compile checking with parens
使用括号增加编译检查
The authors of Spray recommend round parens because they give increased compile checking. This is especially important for DSLs like Spray. By using parens you are telling the compiler that it should only be given a single line; therefore if you accidentally give it two or more, it will complain. Now this isn't the case with curly braces – if for example you forget an operator somewhere, then your code will compile, and you get unexpected results and potentially a very hard bug to find. Below is contrived (since the expressions are pure and will at least give a warning), but makes the point:
Spray 的作者推荐圆括号,因为它们增加了编译检查。这对于像 Spray 这样的 DSL 尤其重要。通过使用括号,你告诉编译器它应该只给出一行;因此,如果您不小心给它两个或更多,它会抱怨。现在大括号不是这种情况——例如,如果你忘记了某个地方的运算符,那么你的代码将被编译,你会得到意想不到的结果,并且可能是一个很难找到的错误。下面是人为的(因为表达式是纯粹的并且至少会给出警告),但说明了这一点:
method {
1 +
2
3
}
method(
1 +
2
3
)
The first compiles, the second gives error: ')' expected but integer literal found. The author wanted to write 1 + 2 + 3.
第一个编译,第二个给出error: ')' expected but integer literal found. 作者想写1 + 2 + 3。
One could argue it's similar for multi-parameter methods with default arguments; it's impossible to accidentally forget a comma to separate parameters when using parens.
有人可能会争辩说,它与带有默认参数的多参数方法类似;使用括号时不可能不小心忘记用逗号分隔参数。
Verbosity
冗长
An important often overlooked note about verbosity. Using curly braces inevitably leads to verbose code since the Scala style guideclearly states that closing curly braces must be on their own line:
关于冗长的一个重要的经常被忽视的注释。使用大括号不可避免地会导致代码冗长,因为Scala 风格指南明确指出,右大括号必须在自己的行上:
… the closing brace is on its own line immediately following the last line of the function.
... 右大括号紧跟在函数的最后一行之后。
Many auto-reformatters, like in IntelliJ, will automatically perform this reformatting for you. So try to stick to using round parens when you can.
许多自动重新格式化程序,如 IntelliJ,会自动为您执行此重新格式化。所以尽量坚持使用圆形括号。
Infix Notation
中缀表示法
When using infix notation, like List(1,2,3) indexOf (2)you can omit parenthesis if there is only one parameter and write it as List(1, 2, 3) indexOf 2. This is not the case of dot-notation.
使用中缀表示法时,List(1,2,3) indexOf (2)如果只有一个参数,则可以省略括号并将其写为List(1, 2, 3) indexOf 2. 这不是点符号的情况。
Note also that when you have a single parameter that is a multi-token expression, like x + 2or a => a % 2 == 0, you have to use parenthesis to indicate the boundaries of the expression.
另请注意,当您有一个作为多标记表达式的单个参数时,例如x + 2或a => a % 2 == 0,您必须使用括号来指示表达式的边界。
Tuples
元组
Because you can omit parenthesis sometimes, sometimes a tuple needs extra parenthesis like in ((1, 2)), and sometimes the outer parenthesis can be omitted, like in (1, 2). This may cause confusion.
因为有时可以省略括号,有时元组需要额外的括号,例如 in ((1, 2)),有时可以省略外括号,例如 in (1, 2)。这可能会引起混淆。
Function/Partial Function literals with case
函数/偏函数字面量 case
Scala has a syntax for function and partial function literals. It looks like this:
Scala 有函数和部分函数字面量的语法。它看起来像这样:
{
case pattern if guard => statements
case pattern => statements
}
The only other places where you can use casestatements are with the matchand catchkeywords:
您可以使用case语句的唯一其他地方是match和catch关键字:
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
You cannot use casestatements in any other context. So, if you want to use case, you needcurly braces. In case you are wondering what makes the distinction between a function and partial function literal, the answer is: context. If Scala expects a function, a function you get. If it expects a partial function, you get a partial function. If both are expected, it gives an error about ambiguity.
您不能case在任何其他上下文中使用语句。所以,如果你想使用case,你需要花括号。如果您想知道函数和部分函数文字之间的区别是什么,答案是:上下文。如果 Scala 需要一个函数,那么你得到的就是一个函数。如果它需要一个偏函数,你就会得到一个偏函数。如果两者都是预期的,则会给出关于歧义的错误。
Expressions and Blocks
表达式和块
Parenthesis can be used to make subexpressions. Curly braces can be used to make blocks of code (this is nota function literal, so beware of trying to use it like one). A block of code consists of multiple statements, each of which can be an import statement, a declaration or an expression. It goes like this:
括号可用于创建子表达式。花括号可用于制作代码块(这不是函数字面量,所以要小心尝试像使用它一样使用它)。一个代码块由多个语句组成,每个语句可以是一个导入语句、一个声明或一个表达式。它是这样的:
{
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 )
So, if you need declarations, multiple statements, an importor anything like that, you need curly braces. And because an expression is a statement, parenthesis may appear inside curly braces. But the interesting thing is that blocks of code are alsoexpressions, so you can use them anywhere insidean expression:
所以,如果你需要声明、多条语句、animport或类似的东西,你需要花括号。并且因为表达式是一个语句,括号可能出现在花括号内。但有趣的是代码块也是表达式,所以你可以在表达式中的任何地方使用它们:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
So, since expressions are statements, and blocks of codes are expressions, everything below is valid:
因此,由于表达式是语句,而代码块是表达式,因此以下所有内容都是有效的:
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...
Where they are not interchangeable
它们不可互换的地方
Basically, you can't replace {}with ()or vice versa anywhere else. For example:
基本上,你不能代替{}用(),反之亦然其他地方。例如:
while (x < 10) { x += 1 }
This is not a method call, so you can't write it in any other way. Well, you can put curly braces insidethe parenthesis for the condition, as well as use parenthesis insidethe curly braces for the block of code:
这不是方法调用,因此您不能以任何其他方式编写它。好吧,您可以将花括号放在的括号内,也可以在代码块的花condition括号内使用括号:
while ({x < 10}) { (x += 1) }
So, I hope this helps.
所以,我希望这会有所帮助。
回答by Theo
There are a couple of different rules and inferences going on here: first of all, Scala infers the braces when a parameter is a function, e.g. in list.map(_ * 2)the braces are inferred, it's just a shorter form of list.map({_ * 2}). Secondly, Scala allows you to skip the parentheses on the last parameter list, if that parameter list has one parameter and it is a function, so list.foldLeft(0)(_ + _)can be written as list.foldLeft(0) { _ + _ }(or list.foldLeft(0)({_ + _})if you want to be extra explicit).
这里有几个不同的规则和推断:首先,当参数是函数时,Scala 推断大括号,例如在list.map(_ * 2)推断大括号中,它只是list.map({_ * 2}). 其次,Scala 允许您跳过最后一个参数列表上的括号,如果该参数列表有一个参数并且它是一个函数,那么list.foldLeft(0)(_ + _)可以写为list.foldLeft(0) { _ + _ }(或者list.foldLeft(0)({_ + _})如果您想更加明确)。
However, if you add caseyou get, as others have mentioned, a partial function instead of a function, and Scala will not infer the braces for partial functions, so list.map(case x => x * 2)won't work, but both list.map({case x => 2 * 2})and list.map { case x => x * 2 }will.
不过,如果你添加case你,正如其他人所提到的,部分功能,而不是一个功能,和Scala不会推断大括号部分功能,因此list.map(case x => x * 2)将无法正常工作,但都list.map({case x => 2 * 2})和list.map { case x => x * 2 }意志。
回答by olle kullberg
There is an effort from the community to standardize the usage of braces and parentheses, see Scala Style Guide (page 21): http://www.codecommit.com/scala-style-guide.pdf
社区努力标准化大括号和圆括号的使用,请参阅 Scala 样式指南(第 21 页):http: //www.codecommit.com/scala-style-guide.pdf
The recommended syntax for higher order methods calls is to always use braces, and to skip the dot:
高阶方法调用的推荐语法是始终使用大括号,并跳过点:
val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }
For "normal" metod calls you should use the dot and parentheses.
对于“正常”方法调用,您应该使用点和括号。
val result = myInstance.foo(5, "Hello")
回答by lcn
I don't think there is anything particular or complex about curly braces in Scala. To master the seeming-complex usage of them in Scala, just keep a couple of simple things in mind:
我不认为 Scala 中的花括号有什么特别或复杂的地方。要掌握它们在 Scala 中看似复杂的用法,只需记住以下几个简单的事情:
- curly braces form a block of code, which evaluates to the last line of code (almost all languages do this)
- a function if desired can be generated with the block of code (follows rule 1)
- curly braces can be omitted for one-line code except for a case clause (Scala choice)
- parentheses can be omitted in function call with code block as a parameter (Scala choice)
- 花括号形成一个代码块,它计算最后一行代码(几乎所有语言都这样做)
- 如果需要,可以使用代码块生成函数(遵循规则 1)
- 除了 case 子句(Scala 选择)外,单行代码可以省略大括号
- 在以代码块为参数的函数调用中可以省略括号(Scala 选择)
Let's explain a couple of examples per the above three rules:
让我们根据上述三个规则解释几个例子:
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
回答by Lukasz Korzybski
I think it is worth explaining their usage in function calls and why various things happen. As someone already said curly braces define a block of code, which is also an expression so can be put where expression is expected and it will be evaluated. When evaluated, its statements are executed and last's statement value is the result of whole block evaluation (somewhat like in Ruby).
我认为值得解释它们在函数调用中的用法以及为什么会发生各种事情。正如有人已经说过的,花括号定义了一个代码块,它也是一个表达式,因此可以放在需要表达式的地方并对其进行评估。当求值时,它的语句被执行,last 的语句值是整个块求值的结果(有点像在 Ruby 中)。
Having that we can do things like:
有了它,我们可以做这样的事情:
2 + { 3 } // res: Int = 5
val x = { 4 } // res: x: Int = 4
List({1},{2},{3}) // res: List[Int] = List(1,2,3)
Last example is just a function call with three parameters, of which each is evaluated first.
最后一个例子只是一个带有三个参数的函数调用,每个参数首先被评估。
Now to see how it works with function calls let's define simple function that take another function as a parameter.
现在看看它是如何处理函数调用的,让我们定义一个简单的函数,它接受另一个函数作为参数。
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
To call it, we need to pass function that takes one param of type Int, so we can use function literal and pass it to foo:
要调用它,我们需要传递一个接受一个 Int 类型参数的函数,因此我们可以使用函数字面量并将其传递给 foo:
foo( x => println(x) )
Now as said before we can use block of code in place of an expression so let's use it
现在如前所述,我们可以使用代码块代替表达式,所以让我们使用它
foo({ x => println(x) })
What happens here is that code inside {} is evaluated, and the function value is returned as a value of the block evaluation, this value is then passed to foo. This is semantically the same as previous call.
这里发生的是对 {} 内的代码进行求值,函数值作为块求值的值返回,然后将该值传递给 foo。这在语义上与之前的调用相同。
But we can add something more:
但是我们可以添加更多内容:
foo({ println("Hey"); x => println(x) })
Now our code block contains two statements, and because it is evaluated before foo is executed, what happens is that first "Hey" is printed, then our function is passed to foo, "Entering foo" is printed and lastly "4" is printed.
现在我们的代码块包含两个语句,因为它在 foo 执行之前被评估,所以会发生首先打印“Hey”,然后将我们的函数传递给 foo,打印“Entering foo”,最后打印“4” .
This looks a bit ugly though and Scala lets us to skip the parenthesis in this case, so we can write:
不过这看起来有点难看,Scala 让我们在这种情况下跳过括号,所以我们可以这样写:
foo { println("Hey"); x => println(x) }
or
或者
foo { x => println(x) }
That looks much nicer and is equivalent to the former ones. Here still block of code is evaluated first and the result of evaluation (which is x => println(x)) is passed as an argument to foo.
这看起来好多了,相当于以前的。这里仍然首先评估代码块,并将评估结果(即 x => println(x))作为参数传递给 foo。
回答by fjdumont
Because you are using case, you are defining a partial function and partial functions require curly braces.
因为您使用的是case,所以您正在定义一个部分函数,而部分函数需要花括号。
回答by samthebest
Increased compile checking with parens
使用括号增加编译检查
The authors of Spray, recommend that round parens give increased compile checking. This is especially important for DSLs like Spray. By using parens you are telling the compiler that it should only be given a single line, therefore if you accidentally gave it two or more, it will complain. Now this isn't the case with curly braces, if for example, you forget an operator somewhere your code will compile, you get unexpected results and potentially a very hard bug to find. Below is contrived (since the expressions are pure and will at least give a warning), but makes the point
Spray 的作者建议圆括号增加编译检查。这对于像 Spray 这样的 DSL 尤其重要。通过使用括号,你告诉编译器它应该只给出一行,因此如果你不小心给了它两行或更多行,它会抱怨。现在花括号不是这种情况,例如,如果您忘记了代码将编译的某个地方的运算符,您会得到意想不到的结果,并且可能是一个很难找到的错误。下面是人为的(因为表达是纯粹的,至少会给出警告),但说明了这一点
method {
1 +
2
3
}
method(
1 +
2
3
)
The first compiles, the second gives error: ')' expected but integer literal found.the author wanted to write 1 + 2 + 3.
第一个编译,第二个给error: ')' expected but integer literal found.了作者想写的1 + 2 + 3。
One could argue it's similar for multi-parameter methods with default arguments; it's impossible to accidentally forget a comma to separate parameters when using parens.
有人可能会争辩说,它与带有默认参数的多参数方法类似;使用括号时不可能不小心忘记用逗号分隔参数。
Verbosity
冗长
An important often overlooked note about verbosity. Using curly braces inevitably leads to verbose code since the scala style guide clearly states that closing curly braces must be on their own line: http://docs.scala-lang.org/style/declarations.html"... the closing brace is on its own line immediately following the last line of the function." Many auto-reformatters, like in Intellij, will automatically perform this reformatting for you. So try to stick to using round parens when you can. E.g. List(1, 2, 3).reduceLeft{_ + _}becomes:
关于冗长的一个重要的经常被忽视的注释。使用大括号不可避免地会导致冗长的代码,因为 Scala 风格指南明确指出,右大括号必须在自己的行上:http: //docs.scala-lang.org/style/declarations.html"... 右大括号紧跟在函数的最后一行之后。” 许多自动重新格式化程序,如 Intellij,会自动为您执行此重新格式化。所以尽量坚持使用圆形括号。例如List(1, 2, 3).reduceLeft{_ + _}变成:
List(1, 2, 3).reduceLeft {
_ + _
}
回答by keitine
With braces, you got semicolon induced for you and parentheses not. Consider takeWhilefunction, since it expects partial function, only {case xxx => ??? }is valid definition instead of parentheses around case expression.
使用大括号,您会得到分号,而括号则不会。考虑takeWhile函数,因为它需要部分函数,所以只有{case xxx => ??? }有效的定义而不是 case 表达式周围的括号。

