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

Gav*_*mun 77 scala list

我有一个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)
}
Run Code Online (Sandbox Code Playgroud)

Ant*_*jdr 135

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

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

编辑:

scala> remove(2,List(2,2,2))
res0: List[Int] = List(2, 2)
Run Code Online (Sandbox Code Playgroud)

喜欢魅力:-).

  • 太好了!我会在列表中添加另外两个,以明确只删除一个元素. (15认同)

小智 33

您可以使用filterNot方法.

val data = "test"
list = List("this", "is", "a", "test")
list.filterNot(elm => elm == data)
Run Code Online (Sandbox Code Playgroud)

  • 这将删除所有等于"test"的元素 - 而不是要求的元素;) (19认同)
  • 最初的问题是如何删除SINGLE实例.并非所有情况. (12认同)

Fra*_*mas 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)
Run Code Online (Sandbox Code Playgroud)

并作为方法:

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

  • @James Petry - 如果你在空列表上调用`tail`,你会得到一个异常:`scala> List().tail java.lang.UnsupportedOperationException:empty list`的尾部.但是在空列表中`drop(1)`会返回一个空列表. (6认同)
  • 如果列表为空(即没有`head`),`tail`会抛出异常.空列表上的`drop(1)`只会产生另一个空列表. (3认同)
  • 值得注意的是,`left ::: right.drop(1)`比使用`isEmpty`的if语句短. (2认同)
  • 谢谢,是否有任何情况比 .tail 更喜欢 .drop(1) ,反之亦然? (2认同)

Rex*_*err 8

不幸的是,集合了层次本身成位了乱七八糟的-List.因为ArrayBuffer它的工作方式就像你希望的那样:

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

但是,遗憾的是,List最终采用了一种filterNot风格的实现,从而做了"错误的事情",并向你发出了一个弃权警告(足够明智,因为它实际上正在发生filterNot):

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

所以可以说最容易做的就是转换List成一个能够做到这一点的集合,然后再转换回来:

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

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

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


Sua*_*GLU 5

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