是否有任何方法可以从Scala n th element
删除每个List
?
我希望我们可以在filter
方法中执行此操作,并通过编写逻辑返回另一个列表。但这是一种有效的方法吗?
答案 0 :(得分:7)
Simplest so far, I think
def removeNth[A](myList: List[A], n: Int): List[A] =
myList.zipWithIndex collect { case (x,i) if (i + 1) % n != 0 => x }
collect
is an oft-forgotten gem that takes a partial function as its second argument, maps elements with that function and ignores those that are not in its domain.
答案 1 :(得分:2)
Simply:
list.zipWithIndex
.filter { case (_, i) => (i + 1) % n != 0 }
.map { case (e, _) => e }
答案 2 :(得分:1)
一种没有索引的方法,将列表分成长度为nth
的块,
xs.grouped(nth).flatMap(_.take(nth-1)).toList
从grouped
发送的每个块中,我们最多可以使用nth-1
个项目。
这另一种方法效率不高(请注意@ Alexey Romanov的评论),使用for desrehears进入flatMap
和withFilter
( lazy 过滤器) ,
for (i <- 0 until xs.size if i % nth != nth-1) yield xs(i)
答案 3 :(得分:1)
这是一个没有索引的递归实现。
def drop[A](n: Int, lst: List[A]): List[A] = {
def dropN(i: Int, lst: List[A]): List[A] = (i, lst) match {
case (0, _ :: xs) => dropN(n, xs)
case (_, x :: xs) => x :: dropN(i - 1, xs)
case (_, x) => x
}
dropN(n, lst)
}
答案 4 :(得分:1)
另一个替代方案,接近@ elm的回答,但考虑到drop(1)
列表要比take
几乎整个列表快得多:
def remove[A](xs: List[A], n: Int) = {
val (firstPart, rest) = xs.splitAt(n - 1)
firstPart ++ rest.grouped(n).flatMap(_.drop(1))
}
答案 5 :(得分:1)
以下是使用累加器的List的尾递归实现:
import scala.annotation.tailrec
def dropNth[A](lst: List[A], n: Int): List[A] = {
@tailrec
def dropRec(i: Int, lst: List[A], acc: List[A]): List[A] = (i, lst) match {
case (_, Nil) => acc
case (1, x :: xs) => dropRec(n, xs, acc)
case (i, x :: xs) => dropRec(i - 1, xs, x :: acc)
}
dropRec(n, lst, Nil).reverse
}
更新:正如评论中所述,我在大型(1 to 5000000).toList
输入中尝试了其他解决方案。 zipWithIndex filter/collect
OutOfMemoryError
失败的人StackOverflowError
失败而{尾部}失败导致::
失败。我使用List cons(tailrec
)和ListBuffer
效果很好。
这是因为zipping-with-index会创建新的OOM
并附加元组,从而导致::
。
递归只有500万级递归,这对于堆栈来说太多了。
tail-recursive不会创建任何不必要的对象,并且在O(n)
中有效地创建了两个输入副本(即,2 * 5百万个x :: acc
个实例)。第一种是创建过滤后的元素,这些元素的顺序相反,因为输出前置O(1)
(在List
中,而附加O(n)
是undo add
)。第二个就是递归输出的反转。
答案 6 :(得分:1)
最简单的解决方案
scala> def dropNth[T](list:List[T], n:Int) :List[T] = {
| list.take(n-1):::list.drop(n)
| }
答案 7 :(得分:0)
另一种方法:为List创建一个完全符合您需求的函数。这与Martin的dropNth函数相同,但不需要O(n)reverse
:
import scala.collection.mutable.ListBuffer
implicit class improvedList[A](xs: List[A]) {
def filterAllWhereIndex(n: Int): List[A] = {
var i = 1
var these = xs
val b = new ListBuffer[A]
while (these.nonEmpty) {
if (i != n) {
b += these.head
i += 1
} else i = 1
these = these.tail
}
b.result
}
}
(1 to 5000000).toList filterAllWhereIndex 3
如果你想要有效率,这就可以了。另外,它可以用作中缀运算符,如上所示。这是一个很好的模式,以避免使用zipWithIndex
,这似乎有点沉重的时间和空间。