例如假设我有
for (line <- myData) {
println("}, {")
}
有没有办法让最后一行打印
println("}")
答案 0 :(得分:28)
您是否可以重构代码以利用内置的mkString
?
scala> List(1, 2, 3).mkString("{", "}, {", "}")
res1: String = {1}, {2}, {3}
答案 1 :(得分:19)
在进一步讨论之前,我建议您避免println
进行理解。它有时可以用于跟踪集合中间发生的错误,但否则会导致代码难以重构和测试。
更一般地说,如果您可以限制任何类型的副作用发生的地方,生活通常会变得更容易。所以而不是:
for (line <- myData) {
println("}, {")
}
你可以写:
val lines = for (line <- myData) yield "}, {"
println(lines mkString "\n")
我也想在这里猜测你想要输出中每一行的内容!
val lines = for (line <- myData) yield (line + "}, {")
println(lines mkString "\n")
如果你直接使用mkString
,那么你还会感觉更好 - 这就是它的用途!
val lines = myData.mkString("{", "\n}, {", "}")
println(lines)
请注意我们如何首先生成String
,然后在单个操作中打印它。这种方法可以很容易地分成单独的方法,用于在类上实现toString
,或者在测试中检查生成的String。
答案 2 :(得分:13)
我完全赞同之前所说的关于使用mkstring
的内容,并区分第一次迭代而不是最后一次迭代。你还需要区分最后一个,scala集合有一个init
方法,它返回除了最后一个元素之外的所有元素。
所以你可以做到
for(x <- coll.init) workOnNonLast(x)
workOnLast(coll.last)
(init
和last
与头部和尾部相反,它们是第一个,而且除了第一个之外。但是请注意,取决于结构,它们可能是昂贵的。在Vector
,所有这些都很快。在List
上,虽然头部和尾部基本上是免费的,但init
和last
在列表的长度上都是线性的。当集合可能为空时,headOption
和lastOption
可以帮助您,将workOnlast
替换为
for (x <- coll.lastOption) workOnLast(x)
答案 3 :(得分:6)
您可以将TraversableOnce
特征的addString函数作为示例。
def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = {
var first = true
b append start
for (x <- self) {
if (first) {
b append x
first = false
} else {
b append sep
b append x
}
}
b append end
b
}
在您的情况下,分隔符为}, {
,结尾为}
答案 4 :(得分:2)
如果您不想使用内置的mkString函数,可以使用类似
的内容for (line <- lines)
if (line == lines.last) println("last")
else println(line)
更新:正如评论中提到的didierd一样,这个解决方案是错误的,因为最后一个值可以多次出现,他在answer中提供了更好的解决方案。
对于Vectors
来说很好,因为last
函数对它们采用“有效恒定时间”,对于Lists
,它需要线性时间,因此您可以使用模式匹配
@tailrec
def printLines[A](l: List[A]) {
l match {
case Nil =>
case x :: Nil => println("last")
case x :: xs => println(x); printLines(xs)
}
}
答案 5 :(得分:1)
其他答案正确地指向mkString
,对于正常数量的数据,我也会使用它。
但是,mkString
通过StringBuilder
在内存中构建(累积)最终结果。这并不总是可取的,这取决于我们拥有的数据量。
在这种情况下,如果我们想要的只是“打印”,我们不需要先构建大结果(也许我们甚至想避免这种情况)。
考虑这个辅助函数的实现:
def forEachIsLast[A](iterator: Iterator[A])(operation: (A, Boolean) => Unit): Unit = {
while(iterator.hasNext) {
val element = iterator.next()
val isLast = !iterator.hasNext // if there is no "next", this is the last one
operation(element, isLast)
}
}
迭代遍历所有元素,并使用布尔值依次调用operation
依次传递每个元素。如果传递的元素是最后一个,则值为true
。
在你的情况下,它可以像这样使用:
forEachIsLast(myData) { (line, isLast) =>
if(isLast)
println("}")
else
println("}, {")
}
我们在这里有以下优势: