web-dev-qa-db-fra.com

Qu'est-ce qu'une méthode Scala idiomatique pour "supprimer" un élément d'une liste immuable?

J'ai une liste, qui peut contenir des éléments comparables. Je voudrais une liste similaire, mais avec un élément supprimé. Donc, de (A, B, C, B, D), j'aimerais pouvoir "supprimer" un seul B pour obtenir, par exemple, (A, C, B, D). L'ordre des éléments dans le résultat n'a pas d'importance.

J'ai un code de travail, écrit de manière inspirée du LISP dans Scala. Y a-t-il un moyen plus idiomatiquede faire cela?

Le contexte est un jeu de cartes dans lequel deux jeux de cartes standard sont en jeu. Il est donc possible que les cartes soient dupliquées mais jouées une par une.

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)
}
73
Gavilan Comun

Je n'ai pas vu cette possibilité dans les réponses ci-dessus, alors:

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)

Modifier:

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

Comme un charme :-).

127

Vous pouvez utiliser la méthode filterNot.

val data = "test"
list = List("this", "is", "a", "test")
list.filterNot(Elm => Elm == data)
30
Søren Mathiasen

Vous pouvez essayer ceci:

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)

Et comme méthode:

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

Malheureusement, la hiérarchie des collections s'est retrouvée dans le pétrin avec - sur List. Pour ArrayBuffer, cela fonctionne exactement comme vous le souhaiteriez:

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

mais, malheureusement, List a abouti à une implémentation de style filterNot- et fait donc la "mauvaise chose" et vous lance un avertissement de dépréciation (assez raisonnable, puisqu'il s'agit en fait de 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)

Donc, la chose la plus simple à faire est sans doute de convertir List en une collection qui fonctionne correctement, puis de reconvertir:

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

Alternativement, vous pouvez garder la logique du code que vous avez mais rendre le style plus idiomatique:

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)
8
Rex Kerr
 def removeAtIdx[T](idx: Int, listToRemoveFrom: List[T]): List[T] = {
    assert(listToRemoveFrom.length > idx && idx >= 0)
    val (left, _ :: right) = listToRemoveFrom.splitAt(idx)
    left ++ right
 }
4
Suat KARAKUSOGLU
// 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
}
2
Ken Bloom

En tant que solution possible, vous pouvez rechercher un index du premier élément approprié, puis supprimer un élément à cet index:

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

Que diriez-vous

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

Si vous voyez return, il y a quelque chose qui ne va pas.

1
Eugene Yokota

Juste une autre pensée sur la façon de faire cela en utilisant un pli:

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

Solution générique de récursion de queue:

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)
  }
0
Shankar Shastri