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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5636717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:37:57  来源:igfitidea点击:

What is an idiomatic Scala way to "remove" one element from an immutable List?

listscala

提问by Gavilan Comun

I have a List, which may contain elements that will compare as equal. I would like a similar List, but with one element removed. So from (A, B, C, B, D) I would like to be able to "remove" just one B to get e.g. (A, C, B, D). The order of the elements in the result does not matter.

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

I have working code, written in a Lisp-inspired way in Scala. Is there a more idiomatic way to do this?

我有工作代码,在 Scala 中以受 Lisp 启发的方式编写。有没有更惯用的方法来做到这一点?

The context is a card game where two decks of standard cards are in play, so there may be duplicate cards but still played one at a time.

上下文是一种纸牌游戏,其中正在播放两副标准纸牌,因此可能会有重复的纸牌,但仍然一次玩一张。

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

回答by Antonin Brettsnajdr

I haven't seen this possibility in the answers above, so:

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

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)

Edit:

编辑:

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

Like a charm :-).

就像一个魅力:-)。

回答by S?ren Mathiasen

You could use the filterNotmethod.

你可以用这个filterNot方法。

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

回答by Frank S. Thomas

You could try this:

你可以试试这个:

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)

And as method:

作为方法:

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

回答by Rex Kerr

Unfortunately, the collections hierarchy got itself into a bit of a mess with -on List. For ArrayBufferit works just like you might hope:

不幸的是,集合层次结构在-on 上有点混乱List。因为ArrayBuffer它就像您希望的那样工作:

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

but, sadly, Listended up with a filterNot-style implementation and thus does the "wrong thing" andthrows a deprecation warning at you (sensible enough, since it is actually filterNoting):

但是,遗憾的是,List最终得到了一个filterNot-style 实现,因此做了“错误的事情”并向您发出了弃用警告(足够明智,因为它实际上是filterNoting):

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)

So arguably the easiest thing to do is convert Listinto a collection that does this right, and then convert back again:

因此,可以说最简单的方法是转换List为一个正确执行此操作的集合,然后再次转换回来:

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

Alternatively, you could keep the logic of the code you've got but make the style more idiomatic:

或者,您可以保留现有代码的逻辑,但使样式更加惯用:

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)

回答by Suat KARAKUSOGLU

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

回答by Ken Bloom

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

回答by tenshi

As one possible solutions you can find index of the first suitable element and then remove element at this index:

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

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

回答by Eugene Yokota

How about

怎么样

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

If you see return, there's something wrong.

如果你看到了return,那就有问题了。

回答by Shankar Shastri

Generic Tail Recursion Solution:

通用尾递归解决方案:

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

回答by gdiz

Just another thought on how to do this using a fold:

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

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