假设我有一个看起来像这样的列表:
List(0,5,34,0,9,0,0,0)
我想结束的是:
List(0,5,34,0,9)
我正在删除所有尾随零。有没有一种方法,例如:
list.trimRight(_ == 0)
那会实现吗?我可以从头开始编写它,但在我看来它是标准集合附带的东西?
我想出了:
list.take(list.lastIndexWhere(_ != 0) + 1)
有更好的方法吗?
假设我有一个看起来像这样的列表:
List(0,5,34,0,9,0,0,0)
我想结束的是:
List(0,5,34,0,9)
我正在删除所有尾随零。有没有一种方法,例如:
list.trimRight(_ == 0)
那会实现吗?我可以从头开始编写它,但在我看来它是标准集合附带的东西?
我想出了:
list.take(list.lastIndexWhere(_ != 0) + 1)
有更好的方法吗?
如果你想知道哪个最优雅,那我会说
list.reverse.dropWhile(_ == 0).reverse
因为它只需要引用一次输入,并且意图非常明确。
如果你想知道哪个是最有效的,你需要做一些基准测试。结果(对于您的简短测试列表)可能会让您大吃一惊!
// Slowest
191 ns dhg's EnhancedSeq
173 ns user unknown's custom dropRight
91 ns andyczerwonka's take/lastIndexWhere
85 ns Rex's :\ (foldRight) -- see below
60 ns dhg / Daniel's reverse/dropWhile/reverse
52 ns Rex's customDropTrailingZeros -- see below
// Fastest
可能存在一些适度的机器与机器之间的差异,但基本上这是一个花哨的短列表对您没有帮助的情况。如果列表很长,情况可能会发生很大变化。
这是折叠版本(但在大列表中堆栈溢出):
(list :\ list.take(0)){ (x,ys) => if (x==0 && ys.isEmpty) ys else x :: ys }
这是自定义版本(完全非通用 - 仅适用于此特定任务!):
@annotation.tailrec def customDropZeros(
xs: List[Int],
buffer: Array[Int] = new Array[Int](16),
n: Int = 0
): List[Int] = {
if (xs.isEmpty) {
var ys = xs
var m = n
while (m>0 && buffer(m-1)==0) m -= 1
var i = m-1
while (i>=0) {
ys = buffer(i) :: ys
i -= 1
}
ys
}
else {
val b2 = (
if (n<buffer.length) buffer
else java.util.Arrays.copyOf(buffer, buffer.length*2)
)
b2(n) = xs.head
customDropZeros(xs.tail, b2, n+1)
}
}
reverse dropWhile reverse
除非你有充分的理由否则使用。它出奇的快,出奇的清晰。
我想我的答案list.take(list.lastIndexWhere(_ != 0)+1)
是这样做的方法。
scala> val xs = List(0,5,34,0,9,0,0,0)
xs: List[Int] = List(0, 5, 34, 0, 9, 0, 0, 0)
scala> xs.reverse.dropWhile(_ == 0).reverse
res1: List[Int] = List(0, 5, 34, 0, 9)
编辑:
这是一种一次性 (O(n)) 方法,它添加了一个隐式dropWhileRight
方法Seq
class EnhancedSeq[A, Repr <: Seq[A]](seq: SeqLike[A, Repr]) {
def dropRightWhile[That](p: A => Boolean)(implicit bf: CanBuildFrom[Repr, A, That]): That = {
val b = bf(seq.asInstanceOf[Repr])
val buffer = collection.mutable.Buffer[A]()
for (x <- seq) {
buffer += x
if (!p(x)) {
b ++= buffer
buffer.clear()
}
}
b.result
}
}
implicit def enhanceSeq[A, Repr <: Seq[A]](seq: SeqLike[A, Repr]) = new EnhancedSeq(seq)
你就像这样使用它:
scala> List(0,5,34,0,9,0,0,0).dropRightWhile(_ == 0)
res2: List[Int] = List(0, 5, 34, 0, 9)
Scala 中没有这样的方法,并且List
在更改它的“结束”时效率非常低。更喜欢Vector
.
这适用于List
(我的其他建议充满了错误,我将其删除):
list.reverse.dropWhile(_ == 0).reverse
您可以遍历列表,并缓冲 0,直到找到一些非 0。如果找到非 0,则将缓冲区附加到到目前为止的结果中,然后继续。但是如果你的 List 以 0 结尾,你就把最后一个缓冲区扔掉。
但是 - 最后,reverse
仍然需要 a 。
val xs = List(0,5,34,0,9,0,0,0)
import annotation._
@tailrec
def dropRight [T] (l: List[T], p: (T=>Boolean), carry: List[T]=List.empty, buf: List[T]=List.empty): List[T] = {
if (l.isEmpty) carry.reverse else
if (p (l.head)) dropRight (l.tail, p, l.head :: buf ::: carry, List.empty) else
dropRight (l.tail, p, carry, l.head :: buf) }
dropRight (xs, (x: Int) => x != 0)
res122: List[Int] = List(0, 5, 34, 0, 9)
如果您对最后的顺序不感兴趣,并且可以省略“反向”调用,那可能会很有趣,但为什么您只删除最后一个 Ts 呢?
基准:
我进一步增加了尺寸,但图案重复了。