在括号()和大括号{}中传递参数给函数之间的形式区别是什么?

我从《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 }

= >好。


当前回答

因为使用的是case,所以定义的是偏函数,而偏函数需要花括号。

其他回答

因为使用的是case,所以定义的是偏函数,而偏函数需要花括号。

理想编码风格中的圆括号基本上用于单行代码。 但如果特定的代码段是多行,那么使用大括号是更好的方法。

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

花括号形成一个代码块,计算到最后一行代码(几乎所有语言都这样做) 如果需要,可以使用代码块生成函数(遵循规则1) 除了case子句外,单行代码可以省略花括号(Scala选择) 括号可以在函数调用中省略,将代码块作为参数(Scala选择)

让我们根据以上三条规则来解释几个例子:

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

我曾经试着写过这方面的文章,但最后还是放弃了,因为规则有点分散。基本上,你得掌握窍门。

也许最好集中在大括号和圆括号可以互换使用的地方:向方法调用传递参数时。当且仅当方法需要单个参数时,可以将花括号替换为圆括号。例如:

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

List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

然而,要更好地掌握这些规则,你还需要了解更多。

增加了使用paren的编译检查

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:

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
)

第一个编译,第二个给出错误:')'期望但发现整数字面量。作者想写1 + 2 + 3。

有人可能会说,这与带默认参数的多参数方法类似;在使用paren时,不可能不小心忘记用逗号分隔参数。

冗长

关于冗长,一个经常被忽视的重要注意事项。使用花括号不可避免地会导致冗长的代码,因为Scala风格指南明确指出,右花括号必须在它们自己的行上:

闭大括号在最后一个大括号后面的另一行上 函数的直线。

许多自动重新格式化程序(如IntelliJ)将自动为您执行这种重新格式化。所以尽量坚持使用圆括号。

中缀表示法

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

还要注意,当您有一个多令牌表达式的单个参数时,例如x + 2或a => a % 2 == 0,您必须使用圆括号来指示表达式的边界。

元组

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

带大小写的函数字面量

Scala有函数和部分函数字面量的语法。它是这样的:

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

你可以使用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
}

您不能在任何其他上下文中使用case语句。如果你想用case,你需要花括号。如果您想知道函数和部分函数之间的区别是什么,答案是:上下文。如果Scala需要一个函数,你就会得到一个函数。如果它期望一个偏函数,你就得到一个偏函数。如果两者都是预期的,则给出关于歧义的错误。

表达式和块

括号可以用来生成子表达式。花括号可以用来组成代码块(这不是一个字面函数,所以要小心试图像使用它一样使用它)。代码块由多条语句组成,每个语句可以是import语句、声明或表达式。它是这样的:

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

如果你需要声明,多条语句,一个导入或者类似的东西,你需要花括号。由于表达式是语句,括号可以出现在花括号内。但有趣的是,代码块也是表达式,所以你可以在表达式中的任何地方使用它们:

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

因此,由于表达式是语句,代码块是表达式,下面的所有内容都是有效的:

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

它们不能互换的地方

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

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

这不是一个方法调用,所以不能以其他方式编写它。好吧,你可以把花括号放在括号内的条件,以及使用括号在花括号内的代码块:

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

社区正在努力标准化大括号和圆括号的使用,参见Scala风格指南(第21页):http://www.codecommit.com/scala-style-guide.pdf

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

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

对于“普通”方法调用,应该使用点和圆括号。

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