在括号()和大括号{}中传递参数给函数之间的形式区别是什么?
我从《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 }
= >好。
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}将。
我认为有必要解释一下它们在函数调用中的用法以及为什么会发生各种事情。有人已经说过花括号定义了一个代码块,它也是一个表达式,所以可以放在表达式需要的地方,它将被求值。当被求值时,它的语句被执行,最后的语句值是整个块求值的结果(有点像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。
增加了使用paren的编译检查
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
method {
1 +
2
3
}
method(
1 +
2
3
)
第一个编译,第二个给出错误:')'期望但发现整数字面量。作者想写1 + 2 + 3。
有人可能会说,这与带默认参数的多参数方法类似;在使用paren时,不可能不小心忘记用逗号分隔参数。
冗长
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:
List(1, 2, 3).reduceLeft {
_ + _
}