Mam listę, która może zawierać elementy, które będą porównywane jako równe. Chciałbym mieć podobną listę, ale z usuniętym jednym elementem. Tak więc z (A, B, C, B, D) chciałbym móc „usunąć” tylko jedno B, aby uzyskać np. (A, C, B, D). Kolejność elementów w wyniku nie ma znaczenia.
Mam działający kod, napisany w Scali w sposób inspirowany Lispem. Czy jest na to bardziej idiomatyczny sposób?
Kontekstem jest gra karciana, w której w grze znajdują się dwie talie standardowych kart, więc mogą istnieć zduplikowane karty, ale nadal gra się je pojedynczo.
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)
}
List[Card]
w tym pytaniu jest ręka gracza?Odpowiedzi:
W powyższych odpowiedziach nie widziałem takiej możliwości, więc:
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)
Edytować:
scala> remove(2,List(2,2,2)) res0: List[Int] = List(2, 2)
Jak urok :-).
źródło
Możesz użyć tej
filterNot
metody.val data = "test" list = List("this", "is", "a", "test") list.filterNot(elm => elm == data)
źródło
Możesz spróbować tego:
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)
I jako metoda:
def removeInt(i: Int, li: List[Int]) = { val (left, right) = li.span(_ != i) left ::: right.drop(1) }
źródło
left ::: right.drop(1)
jest krótszy niż instrukcja if zisEmpty
.tail
na pustej liście pojawi się wyjątek:scala> List().tail java.lang.UnsupportedOperationException: tail of empty list
.drop(1)
na pustej liście zwraca jednak pustą listę.tail
zgłasza wyjątek, jeśli lista jest pusta (tj. nie mahead
).drop(1)
na pustej liście daje po prostu kolejną pustą listę.Niestety hierarchia kolekcji wpadła w trochę bałagan z
-
włączeniemList
. PonieważArrayBuffer
działa tak, jak możesz mieć nadzieję:scala> collection.mutable.ArrayBuffer(1,2,3,2,4) - 2 res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 3, 2, 4)
ale niestety
List
skończyło się nafilterNot
implementacji w stylu a, przez co robi „złą rzecz” i rzuca ostrzeżenie o dezaprobacie (dość rozsądne, ponieważ tak naprawdę jestfilterNot
):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)
Prawdopodobnie najłatwiejszą rzeczą do zrobienia jest przekonwertowanie
List
na zbiór, który robi to dobrze, a następnie ponowna konwersja:import collection.mutable.ArrayBuffer._ scala> ((ArrayBuffer() ++ List(1,2,3,2,4)) - 2).toList res2: List[Int] = List(1, 3, 2, 4)
Alternatywnie możesz zachować logikę kodu, który masz, ale uczynić styl bardziej idiomatycznym:
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)
źródło
removeInt(5,List(1,2,6,4,5,3,6,4,6,5,1))
plonyList(4, 6, 2, 1, 3, 6, 4, 6, 5, 1)
. Myślę, że nie tego chciałeś.def removeAtIdx[T](idx: Int, listToRemoveFrom: List[T]): List[T] = { assert(listToRemoveFrom.length > idx && idx >= 0) val (left, _ :: right) = listToRemoveFrom.splitAt(idx) left ++ right }
źródło
Co powiesz na
def removeCard(c: Card, cards: List[Card]) = { val (head, tail) = cards span {c!=} head ::: (tail match { case x :: xs => xs case Nil => Nil }) }
Jeśli widzisz
return
, coś jest nie tak.źródło
c
c
, ale tylko najpierw należy usunąć.// 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 }
źródło
Jako jedno z możliwych rozwiązań możesz znaleźć indeks pierwszego odpowiedniego elementu, a następnie usunąć element z tego indeksu:
def removeOne(l: List[Card], c: Card) = l indexOf c match { case -1 => l case n => (l take n) ++ (l drop (n + 1)) }
źródło
span
do zrobienia tego samego.Jeszcze jedna myśl, jak to zrobić za pomocą fałdy:
def remove[A](item : A, lst : List[A]) : List[A] = { lst.:\[List[A]](Nil)((lst, lstItem) => if (lstItem == item) lst else lstItem::lst ) }
źródło
Ogólne rozwiązanie rekurencji ogona:
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) }
źródło
val list : Array[Int] = Array(6, 5, 3, 1, 8, 7, 2) val test2 = list.splitAt(list.length / 2)._2 val res = test2.patch(1, Nil, 1)
źródło
object HelloWorld { def main(args: Array[String]) { var months: List[String] = List("December","November","October","September","August", "July","June","May","April","March","February","January") println("Deleting the reverse list one by one") var i = 0 while (i < (months.length)){ println("Deleting "+months.apply(i)) months = (months.drop(1)) } println(months) } }
źródło
var
s iwhile
pętli nie jest idiomatycznym Scala.