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 + 2
或a => a % 2 == 0
,你必须使用圆括号来表示expression式的边界。
元组
因为有时可以省略括号,所以有时元组需要像((1, 2))
那样的额外的括号,有时外部的括号可以省略,如(1, 2)
。 这可能会导致混淆。
函数/部分函数文字与case
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
,你需要花括号。 如果你想知道是什么区分了函数和部分函数文字,答案是:上下文。 如果斯卡拉期待一个函数,你得到一个函数。 如果它期望一个部分函数,你会得到一个部分函数。 如果两者都是预期的,则会给出一个含糊不清的错误。
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)
- 对于单行代码,可以省略大括号,除了case子句(Scalaselect)
- 在代码块作为参数的函数调用中可以省略圆括号(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 { _ + _ }