什么是从不可变列表中“删除”一个元素的惯用Scala方法?

时间:2011-04-12 14:19:25

标签: list scala

我有一个List,它可能包含比较相等的元素。我想要一个类似的List,但删除了一个元素。所以从(A,B,C,B,D)我希望能够“移除”一个B来获得例如(A,C,B,D)。结果中元素的顺序无关紧要。

我有工作代码,在Scala中用Lisp启发的方式编写。是否有更惯用的方式 这样做?

上下文是一款纸牌游戏,其中有两副标准牌在玩,所以有可能 是重复的卡,但仍然一次播放一个。

def removeOne(c: Card, left: List[Card], right: List[Card]): List[Card] = {
  if (Nil == right) {
    return left
  }
  if (c == right.head) {
    return left ::: right.tail
  }
  return removeOne(c, right.head :: left, right.tail)
}

def removeCard(c: Card, cards: List[Card]): List[Card] = {
  return removeOne(c, Nil, cards)
}

12 个答案:

答案 0 :(得分:130)

我在上面的答案中没有看到这种可能性,所以:

scala> def remove(num: Int, list: List[Int]) = list diff List(num)
remove: (num: Int,list: List[Int])List[Int]

scala> remove(2,List(1,2,3,4,5))
res2: List[Int] = List(1, 3, 4, 5)

编辑:

scala> remove(2,List(2,2,2))
res0: List[Int] = List(2, 2)

喜欢魅力: - )。

答案 1 :(得分:31)

您可以使用filterNot方法。

val data = "test"
list = List("this", "is", "a", "test")
list.filterNot(elm => elm == data)

答案 2 :(得分:15)

你可以试试这个:

scala> val (left,right) = List(1,2,3,2,4).span(_ != 2)
left: List[Int] = List(1)
right: List[Int] = List(2, 3, 2, 4)

scala> left ::: right.tail                            
res7: List[Int] = List(1, 3, 2, 4)

作为方法:

def removeInt(i: Int, li: List[Int]) = {
   val (left, right) = li.span(_ != i)
   left ::: right.drop(1)
}

答案 3 :(得分:8)

不幸的是,集合层次结构在- List上变得有些混乱。对于ArrayBuffer,它的效果就像您希望的那样:

scala> collection.mutable.ArrayBuffer(1,2,3,2,4) - 2
res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 3, 2, 4)

但是,遗憾的是,List结果是filterNot式的实现,因此“错误的东西”会向您发出弃用警告(足够明智,因为它实际上是filterNot ing):

scala> List(1,2,3,2,4) - 2                          
warning: there were deprecation warnings; re-run with -deprecation for details
res1: List[Int] = List(1, 3, 4)

可以说最简单的事情就是将List转换为一个正确执行此操作的集合,然后再转换回来:

import collection.mutable.ArrayBuffer._
scala> ((ArrayBuffer() ++ List(1,2,3,2,4)) - 2).toList
res2: List[Int] = List(1, 3, 2, 4)

或者,您可以保留您所拥有的代码的逻辑,但使风格更加惯用:

def removeInt(i: Int, li: List[Int]) = {
  def removeOne(i: Int, left: List[Int], right: List[Int]): List[Int] = right match {
    case r :: rest =>
      if (r == i) left.reverse ::: rest
      else removeOne(i, r :: left, rest)
    case Nil => left.reverse
  }
  removeOne(i, Nil, li)
}

scala> removeInt(2, List(1,2,3,2,4))
res3: List[Int] = List(1, 3, 2, 4)

答案 4 :(得分:4)

 def removeAtIdx[T](idx: Int, listToRemoveFrom: List[T]): List[T] = {
    assert(listToRemoveFrom.length > idx && idx >= 0)
    val (left, _ :: right) = listToRemoveFrom.splitAt(idx)
    left ++ right
 }

答案 5 :(得分:2)

// throws a MatchError exception if i isn't found in li
def remove[A](i:A, li:List[A]) = {
   val (head,_::tail) = li.span(i != _)
   head ::: tail
}

答案 6 :(得分:1)

怎么样

def removeCard(c: Card, cards: List[Card]) = {
  val (head, tail) = cards span {c!=}   
  head ::: 
  (tail match {
    case x :: xs => xs
    case Nil => Nil
  })
}

如果你看到return,就会出现问题。

答案 7 :(得分:1)

作为一种可能的解决方案,您可以找到第一个合适元素的索引,然后删除此索引处的元素:

def removeOne(l: List[Card], c: Card) = l indexOf c match {
    case -1 => l
    case n => (l take n) ++ (l drop (n + 1))
}

答案 8 :(得分:0)

关于如何使用折叠来做另一个想法:

def remove[A](item : A, lst : List[A]) : List[A] = {
    lst.:\[List[A]](Nil)((lst, lstItem) => 
       if (lstItem == item) lst else lstItem::lst )
}

答案 9 :(得分:0)

Generic Tail Recursion解决方案:

def removeElement[T](list: List[T], ele: T): List[T] = {
    @tailrec
    def removeElementHelper(list: List[T],
                            accumList: List[T] = List[T]()): List[T] = {
      if (list.length == 1) {
        if (list.head == ele) accumList.reverse
        else accumList.reverse ::: list
      } else {
        list match {
          case head :: tail if (head != ele) =>
            removeElementHelper(tail, head :: accumList)
          case head :: tail if (head == ele) => (accumList.reverse ::: tail)
          case _                             => accumList
        }
      }
    }
    removeElementHelper(list)
  }

答案 10 :(得分:-3)

val list : Array[Int] = Array(6, 5, 3, 1, 8, 7, 2)
val test2 = list.splitAt(list.length / 2)._2
val res = test2.patch(1, Nil, 1)

答案 11 :(得分:-4)

object HelloWorld {

    def main(args: Array[String]) {

        var months: List[String] = List("December","November","October","September","August", "July","June","May","April","March","February","January")

        println("Deleting the reverse list one by one")

        var i = 0

        while (i < (months.length)){

            println("Deleting "+months.apply(i))

            months = (months.drop(1))

        }

        println(months)

    }

}