Scala 中大括号和括号之间的正式区别是什么?何时应该使用它们?
将参数传递给括号 ()
和大括号 {}
中的函数之间有什么形式区别?
我从《Scala 编程》一书中得到的感受Scala 非常灵活,我应该使用我最喜欢的那个,但我发现有些情况可以编译,而另一些则不能。
例如(只是作为一个例子;我希望任何讨论一般情况的回复,而不仅仅是这个特定的例子):
val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
=>错误:简单表达式的非法开始
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
=>美好的。
What is the formal difference between passing arguments to functions in parentheses ()
and in braces {}
?
The feeling I got from the Programming in Scala book 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.
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我曾经尝试过写这个,但最终放弃了,因为规则有些分散。基本上,你必须掌握它的窍门。
也许最好关注花括号和圆括号可以互换使用的地方:将参数传递给方法调用时。当且仅当该方法需要单个参数时,您可以用圆括号替换大括号。例如:
但是,为了更好地掌握这些规则,您还需要了解更多信息。
使用括号增强编译检查
Spray 的作者推荐圆括号,因为它们可以增强编译检查。这对于像 Spray 这样的 DSL 尤为重要。通过使用括号,您可以告诉编译器应该只给出一行;因此,如果你不小心给了它两个或更多,它就会抱怨。现在,大括号的情况并非如此——例如,如果您在某处忘记了运算符,那么您的代码将编译,并且您会得到意外的结果,并且可能会出现很难找到的错误。下面是人为的(因为表达式是纯的并且至少会给出警告),但要点是:
第一个编译,第二个给出
error: ')' 预期但找到整数文字
。作者想写1 + 2 + 3
。有人可能会说这与具有默认参数的多参数方法类似;使用括号时不可能意外忘记用逗号来分隔参数。
冗长
关于冗长的一个经常被忽视的重要注释。使用大括号不可避免地会导致冗长的代码,因为 Scala 风格指南 明确指出右花括号必须独占一行:
许多自动重新格式化程序(例如 IntelliJ)会自动为您执行此重新格式化。因此,尽可能坚持使用圆括号。
中缀表示法
当使用中缀表示法时,例如
List(1,2,3) indexOf (2)
如果只有一个参数,则可以省略括号并将其写为List(1, 2, 3)indexOf 2。这不是点表示法的情况。
另请注意,当您有一个多标记表达式的参数时,例如
x + 2
或a => a % 2 == 0
,必须用括号来表示表达式的边界。元组
因为有时可以省略括号,所以有时元组需要额外的括号,如
((1, 2))
中,有时外括号可以省略,如(1, 2)
。这可能会引起混乱。带有
case
的函数/部分函数字面量Scala 有函数和部分函数字面量的语法。它看起来像这样:
您可以使用
case
语句的唯一其他地方是使用match
和catch
关键字:您不能使用任何其他上下文中的
case
语句。因此,如果您想使用case
,则需要花括号。如果您想知道函数和部分函数文字之间的区别是什么,答案是:上下文。如果 Scala 需要一个函数,那么您就会得到一个函数。如果它需要一个部分函数,您就会得到一个部分函数。如果两者都是预期的,则会给出有关歧义的错误。表达式和块
括号可用于创建子表达式。大括号可用于构成代码块(这不是函数文字,因此请小心尝试像函数一样使用它)。代码块由多个语句组成,每个语句可以是导入语句、声明或表达式。它是这样的:
因此,如果您需要声明、多个语句、
import
或类似的内容,则需要花括号。由于表达式是一个语句,因此圆括号可能出现在大括号内。但有趣的是,代码块也是表达式,因此您可以在内部表达式的任何地方使用它们:因此,由于表达式是语句,代码块也是表达式,以下所有内容均有效:
在它们不可互换的
情况下 基本上,您不能在其他任何地方将
{}
替换为()
,反之亦然。例如:这不是一个方法调用,所以你不能用任何其他方式编写它。那么,您可以将大括号内部放在
条件
的括号内,也可以将括号内部用于代码块的大括号内: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 parentheses can be used interchangeably: when passing parameters to method calls. You may replace curly braces with parentheses if, and only if, the method expects a single parameter. For example:
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:
The first compiles, the second gives
error: ')' expected but integer literal found
. The author wanted to write1 + 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:
Many auto-reformatters, like in IntelliJ, will automatically perform this reformatting for you. So try to stick to using round parens when you can.
Infix Notation
When using infix notation, like
List(1,2,3) indexOf (2)
you can omit parentheses if there is only one parameter and write it asList(1, 2, 3) indexOf 2
. This is not the case of dot-notation.Note also that when you have a single parameter that is a multi-token expression, like
x + 2
ora => a % 2 == 0
, you have to use parentheses to indicate the boundaries of the expression.Tuples
Because you can omit parentheses sometimes, sometimes a tuple needs extra parentheses like in
((1, 2))
, and sometimes the outer parentheses can be omitted, like in(1, 2)
. This may cause confusion.Function/Partial Function literals with
case
Scala has a syntax for function and partial function literals. It looks like this:
The only other places where you can use
case
statements are with thematch
andcatch
keywords:You cannot use
case
statements in any other context. So, if you want to usecase
, you need curly 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.Expressions and Blocks
Parentheses can be used to make subexpressions. Curly braces can be used to make blocks of code (this is not a 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:
So, if you need declarations, multiple statements, an
import
or anything like that, you need curly braces. And because an expression is a statement, parentheses may appear inside curly braces. But the interesting thing is that blocks of code are also expressions, so you can use them anywhere inside an expression:So, since expressions are statements, and blocks of codes are expressions, everything below is valid:
Where they are not interchangeable
Basically, you can’t replace
{}
with()
or vice versa anywhere else. For example:This is not a method call, so you can’t write it in any other way. Well, you can put curly braces inside the parentheses for the
condition
, as well as use parentheses inside the curly braces for the block of code:这里有几个不同的规则和推论:首先,当参数是函数时,Scala 会推断大括号,例如在
list.map(_ * 2)
中,会推断大括号,它只是list.map({_ * 2})
的较短形式。其次,Scala 允许您跳过最后一个参数列表上的括号,如果该参数列表有一个参数并且它是一个函数,那么可以编写list.foldLeft(0)(_ + _)
作为list.foldLeft(0) { _ + _ }
(或者list.foldLeft(0)({_ + _})
如果你想更加明确)。但是,如果您添加
case
,正如其他人提到的,您会得到一个部分函数而不是函数,并且 Scala 不会推断部分函数的大括号,因此list.map(case x => x * 2)
不起作用,但list.map({case x => 2 * 2})
和list.map { case x => x * 2 }
将会。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 oflist.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, solist.foldLeft(0)(_ + _)
can be written aslist.foldLeft(0) { _ + _ }
(orlist.foldLeft(0)({_ + _})
if you want to be extra explicit).However, if you add
case
you get, as others have mentioned, a partial function instead of a function, and Scala will not infer the braces for partial functions, solist.map(case x => x * 2)
won't work, but bothlist.map({case x => 2 * 2})
andlist.map { case x => x * 2 }
will.社区正在努力标准化大括号和括号的使用,请参阅 Scala 样式指南(第 21 页):http://www.codecommit.com/scala-style-guide.pdf
对于高阶方法调用的推荐语法是始终使用大括号,并跳过点:
对于“正常”方法调用时应使用点和括号。
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
The recommended syntax for higher order methods calls is to always use braces, and to skip the dot:
For "normal" metod calls you should use the dot and parentheses.
我不认为 Scala 中的花括号有什么特殊或复杂的地方。要掌握它们在 Scala 中看似复杂的用法,只需记住一些简单的事情:
让我们解释一下根据上述三个规则举几个例子:
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:
Let's explain a couple of examples per the above three rules:
我认为有必要解释一下它们在函数调用中的用法以及为什么会发生各种事情。正如有人已经说过的,大括号定义了一个代码块,它也是一个表达式,因此可以将其放在需要表达式的位置并对其进行求值。当评估时,它的语句被执行,最后的语句值是整个块评估的结果(有点像Ruby)。
有了这个,我们就可以做这样的事情:
最后一个例子只是一个带有三个参数的函数调用,其中每个参数首先被评估。
现在,为了了解它如何与函数调用一起工作,让我们定义一个接受另一个函数作为参数的简单函数。
要调用它,我们需要传递一个采用 Int 类型参数的函数,因此我们可以使用函数文字并将其传递给 foo:
现在正如之前所说,我们可以使用代码块代替表达式,所以让我们使用它
会发生什么这里是对 {} 内的代码进行求值,并将函数值作为块求值的值返回,然后将该值传递给 foo。这在语义上与之前的调用相同。
但我们可以添加更多内容:
现在我们的代码块包含两个语句,并且因为它在 foo 执行之前进行评估,所以会发生的情况是,首先打印“Hey”,然后我们的函数传递给 foo,打印“Entering foo”最后打印“4”。
不过,这看起来有点难看,Scala 允许我们在这种情况下跳过括号,因此我们可以编写:
or
这看起来更好,并且与前面的等效。这里仍然首先评估代码块,并将评估结果(即 x => println(x))作为参数传递给 foo。
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).
Having that we can do things like:
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.
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:
Now as said before we can use block of code in place of an expression so let's use it
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.
But we can add something more:
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.
This looks a bit ugly though and Scala lets us to skip the parenthesis in this case, so we can write:
or
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.
因为您使用的是
case
,所以您正在定义一个部分函数,并且部分函数需要大括号。Because you are using
case
, you are defining a partial function and partial functions require curly braces.使用括号增强编译检查
Spray 的作者建议使用圆括号增强编译检查。这对于像 Spray 这样的 DSL 尤为重要。通过使用括号,您告诉编译器应该只给出一行,因此,如果您不小心给了它两行或更多行,它会抱怨。现在,花括号的情况并非如此,例如,如果您在代码将编译的地方忘记了运算符,您会得到意外的结果,并且可能会出现很难找到的错误。下面是人为的(因为表达式是纯的,至少会给出警告),但表明了这一点:
第一个编译,第二个给出
error: ')' 预期的错误,但找到了整数文字。
作者想要写成1 + 2 + 3
。有人可能会说这与具有默认参数的多参数方法类似;使用括号时不可能意外忘记用逗号来分隔参数。
冗长
关于冗长的一个重要的、经常被忽视的注释。使用大括号不可避免地会导致冗长的代码,因为 scala 样式指南明确指出右大括号必须在自己的行上: http://docs.scala-lang.org/style/declarations.html“...右大括号位于紧跟函数最后一行的单独一行上。”许多自动重新格式化程序(例如 Intellij 中的)会自动为您执行此重新格式化。因此,尽可能坚持使用圆括号。例如
List(1, 2, 3).reduceLeft{_ + _}
变为: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
The first compiles, the second gives
error: ')' expected but integer literal found.
the author wanted to write1 + 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:理想的编码风格中的括号基本上用于单行代码。
但如果特定的代码是多行的,那么使用大括号是更好的方法。
Parenthesis in an ideal coding style is basically used for single line code.
But if the particular piece of code is multiline then using braces is a better way.
有了大括号,你就会得到分号,而括号则不会。考虑
takeWhile
函数,因为它需要部分函数,所以只有{case xxx =>; ??? }
是有效的定义,而不是 case 表达式周围的括号。With braces, you got semicolon induced for you and parentheses not. Consider
takeWhile
function, since it expects partial function, only{case xxx => ??? }
is valid definition instead of parentheses around case expression.