如何删除Scala集合中的尾随元素?

and*_*nka 4 scala

假设我有一个看起来像这样的列表:

List(0,5,34,0,9,0,0,0)
Run Code Online (Sandbox Code Playgroud)

我想最终得到的是:

List(0,5,34,0,9)
Run Code Online (Sandbox Code Playgroud)

我正在删除所有尾随的零.有没有一种方法,比如:

list.trimRight(_ == 0)
Run Code Online (Sandbox Code Playgroud)

会做到这一点?我可以从头开始编写它,但在我看来,这是std集合中的东西吗?

我提出了:

list.take(list.lastIndexWhere(_ != 0) + 1)
Run Code Online (Sandbox Code Playgroud)

有更好的方法吗?

Rex*_*err 12

如果你想知道哪个是最优雅的,那么我会说

list.reverse.dropWhile(_ == 0).reverse
Run Code Online (Sandbox Code Playgroud)

因为它只需要引用一次输入,意图非常明确.

如果您想知道哪个是最有效的,您需要做一些基准测试.结果(对于您的简短测试列表)可能会让您感到惊讶!

// 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
Run Code Online (Sandbox Code Playgroud)

可能存在一些适度的机器到机器的差异,但基本上这是一种情况,对于简短的列表,花哨并没有帮助你.很长的列表可能会发生很大的变化.

这是折叠版本(但是大型列表上的堆栈溢出):

(list :\ list.take(0)){ (x,ys) => if (x==0 && ys.isEmpty) ys else x :: ys }
Run Code Online (Sandbox Code Playgroud)

这是自定义版本(完全非通用 - 仅适用于此特定任务!):

@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)
  }
}
Run Code Online (Sandbox Code Playgroud)

TL;博士

使用reverse dropWhile reverse除非你有充分的理由,否则.它出人意料地快速且令人惊讶地清晰.


and*_*nka 6

我想我的答案list.take(list.lastIndexWhere(_ != 0)+1)就是这样做的方法.