Scala和括号之间Scala的形式差异是什么,何时应该使用它们?

Jea*_*let 317 syntax scala parentheses braces

将参数传递给括号()和括号中的函数有什么形式上的区别{}

我从Scala编程书中得到的感觉是Scala非常灵活,我应该使用我最喜欢的那个,但我发现有些情况是编译而有些情况则没有.

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

val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
Run Code Online (Sandbox Code Playgroud)

=>错误:非法启动简单表达式

val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
Run Code Online (Sandbox Code Playgroud)

=>很好.

Dan*_*ral 353

我试过一次写这个,但最后我放弃了,因为规则有些分散.基本上,你必须掌握它.

也许最好将注意力集中在花括号和括号可以互换使用的位置:将参数传递给方法调用时.如果且仅当方法需要单个参数时,您可以用花括号替换括号.例如:

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

List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Run Code Online (Sandbox Code Playgroud)

但是,您需要了解更多信息才能更好地掌握这些规则.

使用parens增加编​​译检查

Spray的作者推荐圆形parens因为它们增加了编译检查.这对于像Spray这样的DSL尤为重要.通过使用parens,您告诉编译器它应该只给出一行; 因此,如果你不小心给它两个或更多,它会抱怨.现在这不是花括号的情况 - 例如,如果您忘记了某个地方的运算符,那么您的代码将会编译,并且您会得到意想不到的结果,并且可能是一个非常难以找到的错误.下面是设计的(因为表达式是纯粹的并且至少会给出警告),但是重点是:

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
)
Run Code Online (Sandbox Code Playgroud)

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

有人可能认为它与具有默认参数的多参数方法类似; 使用parens时,不可能不小心忘记用逗号分隔参数.

赘言

关于冗长的一个经常被忽视的重要说明.使用花括号不可避免地导致冗长的代码,因为Scala样式指南明确指出关闭花括号必须在它们自己的行上:

......闭合括号紧跟在函数的最后一行之后.

许多自动重新格式化程序(如IntelliJ)将自动为您重新格式化.因此,尽可能坚持使用圆形的parens.

中缀表示法

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

另请注意,当您有一个多标记表达式的单个参数(如x + 2或)时a => a % 2 == 0,必须使用括号来指示表达式的边界.

元组

因为有时可以省略括号,有时元组需要额外的括号 ((1, 2)),有时候外括号可以省略,比如(1, 2).这可能会引起混淆.

函数/部分函数文字 case

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

{
    case pattern if guard => statements
    case pattern => statements
}
Run Code Online (Sandbox Code Playgroud)

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

object match {
    case pattern if guard => statements
    case pattern => statements
}
Run Code Online (Sandbox Code Playgroud)
try {
    block
} catch {
    case pattern if guard => statements
    case pattern => statements
} finally {
    block
}
Run Code Online (Sandbox Code Playgroud)

您不能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 )
Run Code Online (Sandbox Code Playgroud)

所以,如果你需要声明,多个语句,import或类似的东西,你需要花括号.并且因为表达式是一个语句,所以括号可能出现在花括号内.但有趣的是,代码块也是表达式,因此您可以表达式中的任何位置使用它们:

( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Run Code Online (Sandbox Code Playgroud)

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

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...
Run Code Online (Sandbox Code Playgroud)

它们不可互换的地方

基本上,你不能在其他地方替换{},()反之亦然.例如:

while (x < 10) { x += 1 }
Run Code Online (Sandbox Code Playgroud)

这不是方法调用,因此您无法以任何其他方式编写它.好了,你可以把大括号的括号的condition,以及使用括号的代码块的花括号:

while ({x < 10}) { (x += 1) }
Run Code Online (Sandbox Code Playgroud)

所以,我希望这会有所帮助.

  • 这就是为什么人们认为Scala很复杂.我称自己为Scala爱好者. (50认同)
  • @andyczerwonka 我完全同意,但这是您为灵活性和表达能力付出的自然和不可避免的代价(?)=&gt; Scala 并没有被高估。这是否是任何特定情况的正确选择当然是另一回事。 (3认同)
  • 不必为每个我认为使 Scala 代码更简单的方法引入范围!理想情况下,任何方法都不应该使用 `{}` - 一切都应该是一个纯表达式 (2认同)

The*_*heo 56

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

  • 不仅是最后一个参数列表.例如,`list.foldLeft {0} {_ + _}`有效. (4认同)

oll*_*erg 23

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

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

val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }
Run Code Online (Sandbox Code Playgroud)

对于"正常"的metod调用,您应该使用点和括号.

val result = myInstance.foo(5, "Hello")
Run Code Online (Sandbox Code Playgroud)

  • 实际上,惯例是使用圆括号,该链接是非官方的.这是因为在函数式编程中,所有函数都只是一阶公民,因此不应该区别对待.其次Martin Odersky说你应该尝试只使用中缀作为类似运算符的方法(例如`+`,` - `),而不是像`takeWhile`这样的常规方法.中缀符号的整个要点是允许DSL和自定义运算符,因此应该在这种情况下使用它而不是所有时间. (18认同)

lcn*_*lcn 16

我认为Scala中的花括号没有任何特别或复杂的东西.要掌握Scala中看似复杂的用法,只需记住几个简单的事情:

  1. 花括号形成一个代码块,它代表最后一行代码(几乎所有语言都这样做)
  2. 如果需要,可以使用代码块生成函数(遵循规则1)
  3. 对于单行代码,可以省略花括号,除了case子句(Scala选项)
  4. 在代码块作为参数的函数调用中可省略括号(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
Run Code Online (Sandbox Code Playgroud)


Luk*_*ski 13

我认为值得解释它们在函数调用中的用法以及为什么会发生各种各样的事情.正如有人已经说过花括号限定的代码块,这也可以这样,其中的表达,预计它将被评估放的表达式.评估后,其语句执行和最后的语句值是整个街区评估结果(有点像红宝石).

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

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

最后一个例子只是一个带有三个参数的函数调用,每个参数首先进行评估.

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

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
Run Code Online (Sandbox Code Playgroud)

要调用它,我们需要传递带有一个Int类型参数的函数,因此我们可以使用函数文字并将其传递给foo:

foo( x => println(x) )
Run Code Online (Sandbox Code Playgroud)

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

foo({ x => println(x) })
Run Code Online (Sandbox Code Playgroud)

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

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

foo({ println("Hey"); x => println(x) })
Run Code Online (Sandbox Code Playgroud)

现在我们的代码块包含两个语句,并且因为它在执行foo之前被评估,所以会发生第一个"Hey"打印,然后我们的函数被传递给foo,打印"输入foo",最后打印"4" .

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

foo { println("Hey"); x => println(x) }
Run Code Online (Sandbox Code Playgroud)

要么

foo { x => println(x) }
Run Code Online (Sandbox Code Playgroud)

这看起来更好,相当于前者.这里仍然首先评估代码块,并将评估结果(x => println(x))作为参数传递给foo.

  • 难道只有我。但实际上我更喜欢 `foo({ x =&gt; println(x) })` 的显式性质。也许我太拘泥于我的方式...... (2认同)

fjd*_*ont 7

因为您正在使用case,所以您要定义部分函数,​​而部分函数需要花括号.


sam*_*est 5

使用parens增加了编译检查

Spray的作者建议使用圆括号来增加编译检查。这对于Spray等DSL来说尤其重要。通过使用parens,您是在告诉编译器应该只给它一行,因此,如果您不小心给了它两行或更多行,它将抱怨。现在,花括号不是这种情况,例如,如果您忘记了将在代码中编译的运算符,则会得到意想不到的结果,并且可能会发现很难发现的错误。下面是人为设计的(因为表达式是纯净的,至少会给出警告),但指出了这一点

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
 )
Run Code Online (Sandbox Code Playgroud)

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

有人可能会说,对于带有默认参数的多参数方法来说,这是相似的。使用parens时,会偶然忘记逗号分隔参数的可能性。

细度

关于冗长性的重要提示,经常被忽略。使用花括号不可避免地会导致冗长的代码,因为scala样式指南明确指出,右花括号必须位于自己的行上:http : //docs.scala-lang.org/style/declarations.html “ ...在函数的最后一行之后紧跟着一行。” 像Intellij中一样,许多自动重新格式化程序会自动为您重新格式化。因此,请尽量使用圆形括号。例如List(1, 2, 3).reduceLeft{_ + _}变为:

List(1, 2, 3).reduceLeft {
  _ + _
}
Run Code Online (Sandbox Code Playgroud)