it-swarm.com.de

Was ist ein idiomatischer Scala-Weg, um ein Element aus einer unveränderlichen Liste zu "entfernen"?

Ich habe eine Liste, die Elemente enthalten kann, die als gleich betrachtet werden. Ich hätte gerne eine ähnliche Liste, aber ein Element wurde entfernt. Daher möchte ich aus (A, B, C, B, D) nur ein B "entfernen", um z. (A, C, B, D). Die Reihenfolge der Elemente im Ergebnis spielt keine Rolle.

Ich habe Arbeitscode, der von LISP in Scala inspiriert wurde. Gibt es einen idiomatischeren Weg, dies zu tun?

Der Kontext ist ein Kartenspiel, bei dem zwei Decks von Standardkarten im Spiel sind, so dass es möglicherweise doppelte Karten gibt, die aber immer noch einzeln gespielt werden.

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

Ich habe diese Möglichkeit in den obigen Antworten nicht gesehen.

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)

Bearbeiten:

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

Wie ein Zauber :-).

127

Sie können die filterNot-Methode verwenden.

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

Sie könnten dies versuchen:

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)

Und als Methode:

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

Unglücklicherweise geriet die Kollektionshierarchie mit - auf List in ein Durcheinander. Für ArrayBuffer funktioniert es genau so, wie Sie vielleicht hoffen:

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

leider hat List jedoch eine filterNot- Implementierung erhalten, und das "Falsche" und wirft eine abwertende Warnung auf Sie (vernünftig genug, da es tatsächlich filterNoting ist)

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)

Am einfachsten ist es daher, List in eine Sammlung zu konvertieren, die dies richtig macht, und dann wieder zurück zu konvertieren:

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

Alternativ können Sie die Logik Ihres Codes beibehalten, den Stil jedoch idiotischer gestalten:

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

Als mögliche Lösung können Sie den Index des ersten geeigneten Elements finden und dann das Element an diesem Index entfernen:

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

Wie wäre es mit

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

Wenn Sie return sehen, stimmt etwas nicht.

1
Eugene Yokota

Nur ein weiterer Gedanke, wie man das mit einer Falte macht:

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

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