用括号()
和大括号{}
传递参数给函数在形式上有什么区别?
我从 * Programming in Scala * 书中得到的感觉是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 }
好的。
9条答案
按热度按时间ogq8wdun1#
我曾经试着写过这方面的文章,但最后我放弃了,因为规则有点分散。基本上,你必须掌握它的窍门。
也许最好集中在花括号和圆括号可以互换使用的地方:传递参数给方法呼叫时。当且仅当方法需要单一参数时,您 * 可以 * 将大括号取代为括号。例如:
但是,要更好地掌握这些规则,您还需要了解更多信息。
使用括号增加编译检查
Spray的作者建议使用圆括号,因为圆括号增加了编译检查。这对于像Spray这样的DSL尤其重要。通过使用圆括号,你告诉编译器它应该只被赋予一行;因此,如果你不小心给了它两个或更多,它会抱怨。现在,这不是花括号的情况-例如,如果你忘记了一个运算符的地方,那么你的代码将编译,你会得到意想不到的结果,并可能很难找到一个bug。下面是人为的(因为表达式是纯的,至少会给出一个警告),但重点是:
第一个编译,第二个给出
error: ')' expected but integer literal found
。作者想写1 + 2 + 3
。有人可能会说,对于带有默认参数的多参数方法,情况也是如此;在使用括号时不可能不小心忘记用逗号分隔参数。
详细程度
一个经常被忽略的关于冗长的重要注意事项。使用花括号不可避免地会导致冗长的代码,因为Scala样式指南明确指出,右花括号必须在它们自己的行上:
...右大括号位于函数最后一行之后的单独一行。
许多自动重新格式化程序(如IntelliJ)会自动为您执行这种重新格式化。因此,在可能的情况下,尽量坚持使用圆括号。
中缀符号
在使用中缀表示法时,如
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
陈述式的唯一其他地方是与match
和catch
保留字搭配使用:第一个
case
语句 *。因此,如果要使用case
,您 * 需要 * 花括号。如果您想知道函数和部分函数文字之间的区别,答案是:如果Scala需要一个函数,那么你得到的就是一个函数;如果Scala需要一个partial函数,那么你得到的就是一个partial函数;如果两者都需要,那么它会给出一个关于歧义的错误。表达式和块
圆括号可以用来构造子表达式。大括号可以用来构造代码块(这 * 不是 * 函数文字,所以要注意不要把它当作函数文字来使用)。一个代码块由多个语句组成,每个语句都可以是import语句、声明或表达式。它是这样的:
所以,如果你需要声明、多个语句、
import
或类似的东西,你需要花括号。因为表达式是一个语句,括号可能出现在花括号内。但有趣的是,代码块 * 也 * 是表达式,所以你可以 * 在表达式 * 内 * 的任何地方使用它们:因此,由于表达式是语句,而代码块是表达式,因此下面的一切都是有效的:
不可互换
基本上,在其他任何地方都不能用
()
替换{}
,反之亦然。这不是一个方法调用,所以你不能用任何其他方式编写它。好吧,你可以在
condition
的括号内 * 放入花括号,也可以在代码块的花括号内 * 放入圆括号:所以,我希望这对你有帮助。
whhtz7ly2#
这里有几个不同的规则和推论:首先,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 }
都起作用。liwlm1x93#
社区正在努力标准化大括号和圆括号的用法,请参见Scala样式指南(第21页):http://www.codecommit.com/scala-style-guide.pdf
高阶方法呼叫的建议语法是永远使用大括号,并略过点:
对于“正常”方法调用,应使用点和括号。
chhkpiq44#
我不认为Scala中的花括号有什么特别或复杂的地方。要掌握它们在Scala中看似复杂的用法,只需记住几件简单的事情:
1.花括号形成一个代码块,它计算到代码的最后一行(几乎所有语言都这样做)
1.如果需要,可以使用代码块生成函数(遵循规则1)
1.除了case子句(Scala选项)外,单行代码中可以省略花括号
1.在使用代码块作为参数的函数调用中可以省略括号(Scala选项)
让我们根据上述三条规则解释几个示例:
8qgya5xd5#
我认为有必要解释一下它们在函数调用中的用法,以及为什么会发生各种事情。正如有人已经说过的,花括号定义了一个代码块,它也是一个表达式,所以可以放在期望表达式的地方,并对其进行求值。当求值时,它的语句被执行,last的语句值是整个代码块求值的结果(有点像Ruby)。
有了它,我们可以做以下事情:
最后一个例子是一个带有三个参数的函数调用,首先计算每个参数。
现在,为了了解它如何与函数调用一起工作,让我们定义一个简单的函数,它将另一个函数作为参数。
要调用它,我们需要传递一个带有Int类型参数的函数,因此我们可以使用函数literal并将其传递给foo:
如前所述,我们可以使用代码块来代替表达式,所以让我们使用它
这里发生的事情是对{}中的代码进行求值,函数值作为块求值的值返回,然后将该值传递给foo。这在语义上与前面的调用相同。
但我们还可以再补充一点:
现在,我们的代码块包含两个语句,并且由于在执行foo之前对它求值,因此首先输出“Hey”,然后将我们的函数传递给foo,输出“Entering foo”,最后输出“4”。
这看起来有点难看,Scala允许我们在这种情况下跳过括号,所以我们可以写:
或
这看起来更好,并且与前面的等价。这里仍然是先计算代码块,然后将计算结果(即x =〉println(x))作为参数传递给foo。
cyvaqqii6#
因为您使用的是
case
,所以您定义的是分部函数,而分部函数需要大括号。kg7wmglp7#
使用括号增加编译检查
Spray的作者建议圆括号增加编译检查。这对于像Spray这样的DSL来说尤其重要。通过使用圆括号,你告诉编译器它应该只给出一行,因此如果你不小心给了它两行或更多,它会抱怨。现在花括号不是这种情况,例如,如果你在代码将要编译的地方忘记了一个操作符,你会得到意想不到的结果,并且可能会有一个很难找到的bug。2下面是人为的(因为表达式是纯表达式,至少会给出一个警告),但这是重点
第一个编译,第二个给出作者想要写的
1 + 2 + 3
。有人可能会说,对于带有默认参数的多参数方法,情况也是如此;在使用括号时不可能不小心忘记用逗号分隔参数。
详细程度
一个经常被忽视的关于冗长的重要注意事项。使用花括号不可避免地导致冗长的代码,因为scala风格指南明确指出,结束花括号必须在它们自己的行上:http://docs.scala-lang.org/style/declarations.html“...右大括号在函数的最后一行之后。”许多自动重新格式化程序(如Intellij)会自动为您执行这种重新格式化。因此,在可能的情况下尽量使用圆括号。例如,
List(1, 2, 3).reduceLeft{_ + _}
变为:sirbozc58#
在理想的编码风格中,括号基本上用于单行代码。但是如果特定的代码是多行的,那么使用括号是更好的方法。
gwbalxhn9#
使用大括号,您可以得到分号,而括号不能。考虑
takeWhile
函数,因为它需要部分函数,只有{case xxx => ??? }
是有效的定义,而不是用括号括住case表达式。