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

我有一个列表,其中可能包含比较相等的元素。 我想要一个类似的列表,但删除了一个元素。 所以从(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) } 

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

 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) 

就像一个魅力:-)。

你可以使用filterNot方法。

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

你可以试试这个:

 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) } 

不幸的是,collections层次在List上陷入了一团糟。 对于ArrayBuffer它的工作原理就像你可能希望的那样:

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

但是,可悲的是, ListfilterNot style实现结束,因此做了“错误的事情”, 在您身上抛出了弃用警告(足够明智,因为它实际上是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) 
  def removeAtIdx[T](idx: Int, listToRemoveFrom: List[T]): List[T] = { assert(listToRemoveFrom.length > idx && idx >= 0) val (left, _ :: right) = listToRemoveFrom.splitAt(idx) left ++ right } 
 // 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 } 

作为一种可能的解决scheme,您可以find第一个合适元素的索引,然后在此索引处移除元素:

 def removeOne(l: List[Card], c: Card) = l indexOf c match { case -1 => l case n => (l take n) ++ (l drop (n + 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 ,那就错了。

再想想如何用折叠做这个事情:

 def remove[A](item : A, lst : List[A]) : List[A] = { lst.:\[List[A]](Nil)((lst, lstItem) => if (lstItem == item) lst else lstItem::lst ) } 
 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) 
 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) } }