在Scala中,我倾向于使用val
赋值在许多较小的表达式上编写大型链式表达式。在我的公司,我们已经为这种类型的代码改进了一种风格。这是一个完全人为的例子(想法是显示一个包含大量链式调用的表达式):
import scala.util.Random
val table = (1 to 10) map { (Random.nextInt(100), _) } toMap
def foo: List[Int] =
(1 to 100)
.view
.map { _ + 3 }
.filter { _ > 10 }
.flatMap { table.get }
.take(3)
.toList
Daniel Spiewak的Scala Style Guide(pdf),我一般都喜欢,它表明链式方法调用中的前导点符号可能不好(参见doc:Method Invocation / Higher-Order Functions),尽管它没有直接覆盖这样的多行表达式。
是否有另一种更为接受/惯用的方法来编写上面的函数foo
?
更新:2011年6月28日
下面有很多很棒的答案和讨论。似乎没有100%“你必须这样做”答案,所以我将接受最受欢迎的答案,这是目前的理解方法。就个人而言,我认为我现在要坚持使用前导符号,并接受随之而来的风险。
答案 0 :(得分:16)
这个例子有点不切实际,但对于复杂的表达方式,使用理解通常要清晰得多:
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 = (1 to 100).view.map{3+}.filter{10<}.flatMap{table.get}.take(3).toList
//or
def foo = ((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
答案 1 :(得分:11)
我将整个表达式包装成一组括号,以便在可能的情况下分组并避免使用点,
def foo: List[Int] =
( (1 to 100).view
map { _ + 3 }
filter { _ > 10 }
flatMap { table.get }
take(3)
toList )
答案 2 :(得分:6)
以下是临时性的方法。你不能出错。
(specMember
setInfo subst(env, specMember.info.asSeenFrom(owner.thisType, sym.owner))
setFlag (SPECIALIZED)
resetFlag (DEFERRED | CASEACCESSOR | ACCESSOR | LAZY)
)
正宗的编译源!
答案 3 :(得分:5)
我更喜欢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
选择了随机名称。
选择val
而不是其他提到的解决方案有一个很大的优势:
很明显你的代码是做什么的。在您的示例和大多数其他答案中提到的解决方案中,任何人都不一眼就知道它的作用。一个表达式中的信息太多。只有在@Kevin提到的for-expression中,才有可能选择说出名字,但我不喜欢它们,因为:
答案 4 :(得分:2)
我的规则:如果表达式适合单个(80-120个字符)行,请将其保持在一行并尽可能省略点:
def foo: List[Int] =
(1 to 100).view map { _ + 3 } filter { _ > 10 } flatMap table.get take 3 toList
凯文指出,无点样式可能会改善简洁性(但对于不熟悉它的开发人员可能会损害可读性):
def foo: List[Int] =
(1 to 100).view map{3+} filter{10<} flatMap table.get take 3 toList
如果由于长度需要将表达式分隔为多行,则前导点表示法是完全可以接受的。使用这种表示法的另一个原因是当操作需要单独的注释时。如果你需要在多行上传播一个表达式,由于它的长度或者需要对单个操作进行注释,最好将整个表达式包含在parens中。 (作为Alex Boisvert suggests。在这些情况下,每个(逻辑)操作应该在它自己的行上(即每个操作都在一行上,除非可以通过单个注释简洁地描述多个连续操作):
def foo: List[Int] =
( (1 to 100).view
map { _ + 3 }
filter { _ > 10 }
flatMap table.get
take 3
toList )
这种技术避免了在表达式结尾处使用前导点表示法或调用0-arg方法时可能出现的潜在semicolon inference issues。
答案 5 :(得分:1)
我通常会尽量避免在map
和filter
等内容中使用点。所以我可能会写如下:
def foo: List[Int] =
(1 to 100).view map { x =>
x + 3 } filter { x =>
x > 10 } flatMap { table.get } take(3) toList
前导点符号非常易读。我可能会开始使用它。