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

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

= >好。


当前回答

社区正在努力标准化大括号和圆括号的使用,参见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")

其他回答

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

我认为有必要解释一下它们在函数调用中的用法以及为什么会发生各种事情。有人已经说过花括号定义了一个代码块,它也是一个表达式,所以可以放在表达式需要的地方,它将被求值。当被求值时,它的语句被执行,最后的语句值是整个块求值的结果(有点像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) }

要调用它,我们需要传递一个函数,该函数接受一个Int类型的参数,因此我们可以使用function literal并将它传递给foo:

foo( x => println(x) )

现在就像之前说的,我们可以用代码块来代替表达式,让我们使用它

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

这里发生的事情是,{}内的代码被求值,函数值作为块求值的值返回,然后将这个值传递给foo。这在语义上与前面的调用相同。

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

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

现在我们的代码块包含两个语句,因为它是在foo执行之前求值的,所以首先打印“Hey”,然后将函数传递给foo,打印“进入foo”,最后打印“4”。

这看起来有点丑,Scala允许我们在这种情况下跳过括号,所以我们可以这样写:

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

or

foo { x => println(x) }

这看起来好多了,和前面的一样。这里仍然先计算代码块,并将计算结果(x => println(x))作为参数传递给foo。

我不认为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

社区正在努力标准化大括号和圆括号的使用,参见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")

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

但是,如果添加case,就会得到一个部分函数,而不是其他函数,Scala不会推断部分函数的花括号,所以list。Map (case x => x * 2)将不起作用,但两者都列出。映射({case x => 2 * 2})和列表。映射{case x => x * 2}将。