对于包含大量方法链接的Scala代码,什么是公认/推荐的语法?
在Scala中,我倾向于编写大型链式表达式,而不是使用对于包含大量方法链接的Scala代码,什么是公认/推荐的语法?,scala,syntax,coding-style,method-chaining,Scala,Syntax,Coding Style,Method Chaining,在Scala中,我倾向于编写大型链式表达式,而不是使用val赋值编写许多较小的表达式。在我的公司,我们已经为这种类型的代码发展了一种风格。下面是一个完全人为设计的示例(想法是显示一个包含大量链接调用的表达式): 丹尼尔·斯皮瓦克(Daniel Spiewak)的(pdf),我通常很喜欢,认为链式方法调用中的前导点符号可能不好(参见doc:method Invocation/Higher Order Functions),尽管它没有直接涵盖像这样的多行表达式 是否有另一种更为公认/惯用的方法来编写
val
赋值编写许多较小的表达式。在我的公司,我们已经为这种类型的代码发展了一种风格。下面是一个完全人为设计的示例(想法是显示一个包含大量链接调用的表达式):
丹尼尔·斯皮瓦克(Daniel Spiewak)的(pdf),我通常很喜欢,认为链式方法调用中的前导点符号可能不好(参见doc:method Invocation/Higher Order Functions),尽管它没有直接涵盖像这样的多行表达式
是否有另一种更为公认/惯用的方法来编写上述函数foo
更新日期:2011年6月28日
下面有很多很好的答案和讨论。似乎没有100%的“你必须这样做”答案,所以我将通过投票接受最流行的答案,这是目前的理解方法。就我个人而言,我认为我现在要坚持使用领先的点符号,并接受随之而来的风险。我通常会尽量避免在
map
和filter
等方面使用点。所以我可能会这样写:
def foo: List[Int] =
(1 to 100).view map { x =>
x + 3 } filter { x =>
x > 10 } flatMap { table.get } take(3) toList
前导点符号可读性很强。我可能会开始使用它。我的规则:如果表达式适合一行(80-120个字符),请将其保留在一行,并尽可能省略点:
def foo: List[Int] =
(1 to 100).view map { _ + 3 } filter { _ > 10 } flatMap table.get take 3 toList
def foo = ((1 to 100).view map {3+} filter {10<} flatMap {table.get} take 3).toList
def foo = ((4 to 103).view filter {10<} flatMap {table.get} take 3).toList
def foo = ((11 to 103).view flatMap {table.get} take 3).toList
正如Kevin指出的,无点样式可能会提高简洁性(但可能会损害不熟悉它的开发人员的可读性):
这种技术避免了在表达式末尾使用前导点表示法或调用0-arg方法时可能出现的潜在问题。该示例有点不切实际,但对于复杂表达式,使用理解通常要简单得多:
def foo = {
val results = for {
x <- (1 to 100).view
y = x + 3 if y > 10
z <- table get y
} yield z
(results take 3).toList
}
def foo={
val结果=用于{
x10
z我将整个表达式包装到一组括号中,以便对内容进行分组,并尽可能避免点
def foo: List[Int] =
( (1 to 100).view
map { _ + 3 }
filter { _ > 10 }
flatMap { table.get }
take(3)
toList )
我喜欢大量的val
s:
def foo = {
val range = (1 to 100).view
val mappedRange = range map { _+3 }
val importantValues = mappedRange filter { _ > 10 } flatMap { table.get }
(importantValues take 3).toList
}
因为我不知道您想用代码做什么,所以我为val
s选择了随机名称。
选择val
s而不是上述其他解决方案有很大的优势:
代码的作用显而易见。在您的示例和大多数其他答案中提到的解决方案中,任何人都不知道它的作用。一个表达式中的信息太多。只有在@Kevin提到的for表达式中,才可以选择说出姓名,但我不喜欢它们,因为:
他们需要更多的代码行
由于模式匹配声明的值(我提到过这一点),它们的速度较慢
这只是我的看法,但我觉得它们看起来很丑
这是即兴表演的方式,你不会出错的
(specMember
setInfo subst(env, specMember.info.asSeenFrom(owner.thisType, sym.owner))
setFlag (SPECIALIZED)
resetFlag (DEFERRED | CASEACCESSOR | ACCESSOR | LAZY)
)
正宗的编译器源代码!我还发现前导点符号非常可读,但我不确定这是否只是因为我已经习惯了:)拖尾.toList
是一个轻微的风险,我尝试避免它们,因为它们会干扰分号推断(就像前导点样式一样)@凯文:你能在回答中详细说明这一点吗(也许举个例子)?我知道我在邮件列表上听说过这个问题,但我从来没有亲自提过。评论的要点很好。parens分组、前导点和理解方法都非常好地允许每个操作的评论。我仍然倾向于将.toList
放在parens之外,这对我来说是一个更好的代表代码意图的表示。然后,您可以在不失去太多睡眠的情况下放弃显式键入…我没有考虑使用parens。我想我更喜欢这种样式而不是前导的点表示法,它应该能够巧妙地处理任何分号推断问题。很有意思,但似乎有点像黑客。我更喜欢点,因此更清楚每个lin是什么是的,不必四处寻找几行以外的括号。@luigi-显然,您还需要将缩进和括号结合起来,这样就可以清楚地知道“大画面”中到底发生了什么视觉的clues@Aaron这是我自己喜欢的风格,但无参数方法会给引擎带来麻烦。在这种情况下,最好的方法是将无参数方法应用到的表达式放在括号内,后缀方法用点表示法——就像用(1到100)做的那样.view实际上是。如果不是view
,您可以将1写入100map{…}etc
。另一种方法是将后缀作为最后一个方法放在括号内——就像这里的toList
一样。我没想到你可以将=
放入一个括号中进行理解。突然间,它们更有用了。好信息。我喜欢将其作为另一种语法。re:optimizations--我只是加入了愚蠢的额外操作试图人为地延长示例的时间。@overthink-我知道你做到了:)尽管原则仍然有效!我敢不同意下面的Scala专家的观点,并说我更喜欢你的领导。与下面答案中的所有备选方案相比,理解需要发明我认为在很多情况下不必要的名称s、 用括号括起来的-free一词乍一看并不清楚,它是一个列表上的一组操作,而不是一些副作用(我知道,spit)运营。@Paul-领先的
符号很好,感觉也不错。但是在没有指挥的管弦乐队中表演,或者在没有安全套的情况下进行无偿的性行为也是如此-但是我们不做这些事情有非常明确的原因。我不确定我是否接受这些类比-我觉得你似乎过度紧张了,而事实却很糟糕.notation的侧面是可管理的。您可能还想看看Kevin Wright在这里对我的类似问题的回答:)/refactoring functional scala的布局,其中领先的
def foo = ((1 to 100).view map {3+} filter {10<} flatMap {table.get} take 3).toList
def foo = ((4 to 103).view filter {10<} flatMap {table.get} take 3).toList
def foo = ((11 to 103).view flatMap {table.get} take 3).toList
def foo: List[Int] =
( (1 to 100).view
map { _ + 3 }
filter { _ > 10 }
flatMap { table.get }
take(3)
toList )
def foo = {
val range = (1 to 100).view
val mappedRange = range map { _+3 }
val importantValues = mappedRange filter { _ > 10 } flatMap { table.get }
(importantValues take 3).toList
}
(specMember
setInfo subst(env, specMember.info.asSeenFrom(owner.thisType, sym.owner))
setFlag (SPECIALIZED)
resetFlag (DEFERRED | CASEACCESSOR | ACCESSOR | LAZY)
)