Pregunta ¿Cuál es una manera idiomática de Scala de "eliminar" un elemento de una lista inmutable?


Tengo una lista, que puede contener elementos que se compararán como iguales. Me gustaría una Lista similar, pero con un elemento eliminado. Entonces, desde (A, B, C, B, D) me gustaría poder "eliminar" solo una B para obtener, p. (A, C, B, D). El orden de los elementos en el resultado no importa.

Tengo un código de trabajo, escrito en una forma inspirada en Lisp en Scala. ¿Hay una manera más idiomática? ¿para hacer esto?

El contexto es un juego de cartas donde dos barajas de cartas estándar están en juego, por lo que puede ser cartas duplicadas, pero todavía se juegan una a la vez.

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
2018-04-12 14:19


origen


Respuestas:


No he visto esta posibilidad en las respuestas anteriores, entonces:

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)

Editar:

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

Como un encanto :-).


125
2018-04-12 19:47



Podría usar el método filterNot.

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

30
2018-06-27 18:30



Puedes intentar esto:

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)

Y como método:

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

15
2018-04-12 14:53



Desafortunadamente, la jerarquía de colecciones se metió en un lío con - en List. por ArrayBuffer funciona de la manera que podrías esperar:

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

pero tristemente, List terminó con un filterNotestilo de implementación y por lo tanto hace lo "incorrecto" y lanza una advertencia de desaprobación hacia usted (lo suficientemente sensible, ya que es en realidad filterNotEn g):

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)

Así que podría decirse que lo más fácil de hacer es convertir List en una colección que hace esto bien, y luego convertir de nuevo:

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

Alternativamente, puedes mantener la lógica del código que tienes pero hacer el estilo más idiomático:

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)

7
2018-04-12 14:37



 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
2017-08-04 21:41



// 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
2018-04-12 14:45



Como una posible solución, puede encontrar el índice del primer elemento adecuado y luego eliminar el elemento en este índice:

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

1
2018-04-12 14:49



Qué tal si

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

Si tú ves return, algo está mal.


1
2018-04-12 14:38



Solo otro pensamiento sobre cómo hacer esto usando un doblez:

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

0
2018-05-12 23:24



Solución de recurrencia de cola genérica:

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
2018-05-28 15:15