Scala在括号和括号之间有什么正式的区别,什么时候使用?

在括号()和大括号{}传递参数到函数的forms区别是什么?

我从Scala编程书中得到的感觉是,Scala非常灵活,我应该使用我最喜欢的那个,但是我发现某些情况下编译,而另一些情况下却不是。

例如(只是作为一个例子,我将不胜感激讨论一般情况的任何回应,而不仅仅是这个特定的例子):

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

=>错误:简单expression式的非法启动

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

=>罚款。

我曾试着写这个,但最后我放弃了,因为规则有点弥散。 基本上,你必须得到它的窍门。

也许最好把注意力集中在可以交替使用大括号和括号的地方:将parameter passing给方法调用。 当且仅当方法期望单个参数时, 可以用大括号replace括号。 例如:

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

但是,为了更好地掌握这些规则,还需要了解更多信息。

增加与parens的编译检查

Spray的作者推荐使用round parens,因为它们增加了编译检查。 对于像Spray这样的DSL来说,这一点尤为重要。 通过使用parens,你告诉编译器它只应该被赋予一行; 所以如果你不小心给了它两个或更多,它会抱怨。 现在,花括号不是这种情况 – 例如,如果你在某个地方忘记了一个操作符,那么你的代码将会被编译,并且你会得到意想不到的结果,并且可能会发现一个很难的bug。 下面是devise的(因为expression式是纯粹的,并且至less会给出警告),但是要说明一点:

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

第一次编译,第二次给出error: ')' expected but integer literal found 。 作者想写1 + 2 + 3

有人可能会争辩说,它与默认参数的多参数方法是相似的; 使用parens时,不可能忘记逗号来分隔参数。

赘言

一个重要的经常被忽视的有关详细的说明。 使用大括号不可避免地导致冗长的代码,因为斯卡拉风格指南明确指出,大括号的花括号必须在自己的路线:

…紧接在该函数的最后一行之后的右括号是在它自己的行上。

许多自动格式转换器,如IntelliJ,会自动为您执行这种重新格式化。 所以尽可能地坚持使用圆形包袱。

中缀符号

当使用中缀表示法时,如List(1,2,3) indexOf (2) ,如果只有一个参数,则可以省略括号,并将其写为List(1, 2, 3) indexOf 2 。 这不是点符号的情况。

还要注意的是,当你有一个单一的参数是一个多记号expression式,比如x + 2a => a % 2 == 0 ,你必须使用圆括号来表示expression式的边界。

元组

因为有时可以省略括号,所以有时元组需要像((1, 2))那样的额外的括号,有时外部的括号可以省略,如(1, 2) 。 这可能会导致混淆。

函数/部分函数文字与case

Scala有一个函数和部分函数文字的语法。 它看起来像这样:

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

唯一可以使用case语句的其他地方是matchcatch关键字:

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

您不能在任何其他情况下使用case语句 。 所以,如果你想用case ,你需要花括号。 如果你想知道是什么区分了函数和部分函数文字,答案是:上下文。 如果斯卡拉期待一个函数,你得到一个函数。 如果它期望一个部分函数,​​你会得到一个部分函数。 如果两者都是预期的,则会给出一个含糊不清的错误。

expression式和块

括号可以用来表示子expression式。 花括号可以用来制作代码块(这不是一个函数文字,所以要小心使用它像一个)。 一个代码块由多个语句组成,每个语句都可以是一个导入语句,一个声明或一个expression式。 它是这样的:

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

所以,如果你需要声明,多个语句,一个import或类似的东西,你需要花括号。 而因为一个expression式是一个陈述,括号内可能会出现括号。 但有趣的是代码块也是expression式,所以你可以expression式中的任何地方使用它们:

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

所以,由于expression式是语句,并且代码块是expression式,所以下面的所有内容都是有效的:

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

他们不能互换

基本上,你不能用()replace{} ,反之亦然。 例如:

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

这不是一个方法调用,所以你不能以任何其他方式写它。 那么,你可以在condition括号加上花括号,并且在代码块的大括号使用括号:

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

所以,我希望这有助于。

在这里有几个不同的规则和推论:首先,当参数是一个函数时,Scala推断大括号,例如在list.map(_ * 2)中推断大括号,它只是一个简短的list.map({_ * 2})formslist.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 }都会。

标准化大括号和圆括号的用法,请参阅Scala样式指南(第21页): http : //www.codecommit.com/scala-style-guide.pdf

高阶方法调用的推荐语法是始终使用大括号,并跳过点:

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

对于“正常”metod调用,您应该使用圆点和圆括号。

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

我认为值得在函数调用中解释它们的用法,以及为什么会发生各种事情。 正如已经有人所说,花括号定义了一个代码块,这也是一个expression式,所以可以放在需要expression的地方,它将被评估。 评估时,它的语句被执行,最后的语句值是整个块评估的结果(有点像在Ruby中)。

有了我们可以做的事情如:

 2 + { 3 } // res: Int = 5 val x = { 4 } // res: x: Int = 4 List({1},{2},{3}) // res: List[Int] = List(1,2,3) 

最后一个例子只是一个带有三个参数的函数调用,其中每个参数都是先评估的。

现在看看它如何与函数调用一起工作,让我们定义一个简单的函数,将另一个函数作为参数。

 def foo(f: Int => Unit) = { println("Entering foo"); f(4) } 

要调用它,我们需要传递带有一个Inttypes参数的函数,所以我们可以使用函数literal并将它传递给foo:

 foo( x => println(x) ) 

现在如前所述,我们可以使用代码块代替expression式,所以我们使用它

 foo({ x => println(x) }) 

这里发生的是评估{}内的代码,并将函数值作为块评估的值返回,然后将此值传递给foo。 这在语义上与以前的调用相同。

但是我们可以添加更多内容:

 foo({ println("Hey"); x => println(x) }) 

现在我们的代码块包含两个语句,并且因为在foo被执行之前被评估,所以会发生的是首先打印“Hey”,然后我们的函数被传递给foo,“inputfoo”被打印并且最后打印“4” 。

这看起来有点丑陋,Scala让我们跳过括号,所以我们可以这样写:

 foo { println("Hey"); x => println(x) } 

要么

 foo { x => println(x) } 

这看起来好多了,和前者相当。 这里首先评估代码块,并将评估结果(即x => println(x))作为parameter passing给foo。

我不认为在Scala中有什么特别的或复杂的花括号。 掌握在Scala中看似复杂的用法,记住几件简单的事情:

  1. 花括号组成了一块代码块,代码的最后一行(几乎所有的语言都这样做)
  2. 如果需要,可以用代码块生成一个函数(遵循规则1)
  3. 对于单行代码,可以省略大括号,除了case子句(Scalaselect)
  4. 在代码块作为参数的函数调用中可以省略圆括号(Scalaselect)

按照上述三个规则来解释几个例子:

 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, // ie tupleList.takeWhile({ case (s1, s2) => s1 == s2 }) val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 } // curly braces omission, ie List(1, 2, 3).reduceLeft({_+_}) List(1, 2, 3).reduceLeft(_+_) // parentheses omission, ie 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, ie List(1, 2, 3).foldLeft(0)({_ + _}) List(1, 2, 3).foldLeft(0)(_ + _) // parentheses omission, ie 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 // ie foo({ println("Hey"); x => println(x) }) foo { println("Hey"); x => println(x) } // parentheses omission, ie f({x}) def f(x: Int): Int = f {x} // error: missing arguments for method f def f(x: Int): Int = fx 

因为你正在使用case ,你正在定义一个部分函数,​​部分函数需要大括号。

增加与parens的编译检查

喷雾的作者,build议圆形parens增加编​​译检查。 对于像Spray这样的DSL来说,这一点尤为重要。 通过使用parens,你告诉编译器它只应该被赋予一行,所以如果你不小心给了它两个或者更多的话,它会报错。 现在,花括号不是这种情况,例如,如果在代码编译的某个地方忘记了某个操作符,则会得到意想不到的结果,并且可能会发现很难find的错误。 下面是devise的(因为expression式是纯粹的,至less会发出警告),但是要说明一点

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

第一次编译,第二次给出error: ')' expected but integer literal found. 作者想写1 + 2 + 3

有人可能会争辩说,它与默认参数的多参数方法是相似的; 使用parens时,不可能忘记逗号来分隔参数。

赘言

一个重要的经常被忽视的有关详细的说明。 使用大括号不可避免地导致了冗长的代码,因为scala风格指南明确指出,大括号花括号必须在自己的行上:“…右大括号紧跟在函数的最后一行之后的是它自己的行。“ 像Intellij中的许多自动格式化器会自动为您执行这种重新格式化。 所以尽可能地坚持使用圆形包袱。 例如List(1, 2, 3).reduceLeft{_ + _}变成:

 List(1, 2, 3).reduceLeft { _ + _ }