Değişmez bir Listeden bir öğeyi "kaldırmanın" deyimsel bir Scala yolu nedir?


86

Eşit olarak karşılaştıracak öğeler içerebilen bir Listem var. Benzer bir Liste istiyorum, ancak bir öğe kaldırıldı. Yani (A, B, C, B, D) 'den, örneğin (A, C, B, D) elde etmek için sadece bir B'yi "kaldırabilmek" istiyorum. Sonuçtaki öğelerin sırası önemli değil.

Scala'da Lisp'den esinlenerek yazılmış bir çalışma kodum var. Bunu yapmanın daha deyimsel bir yolu var mı?

Bağlam, iki deste standart kartın oyunda olduğu bir kart oyunudur, bu nedenle yinelenen kartlar olabilir, ancak yine de birer birer oynanır.

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

Sonuç Listesinin sırasının bu özel durumda önemli olmadığına dair bir not eklendi.
Gavilan Comun

yani List[Card]bu soru bir oyuncunun eli mi?
Ken Bloom

@Ken Bloom, evet bu doğru bir oyuncunun eli.
Gavilan Comun

Biliyorsunuz, bir süre bunun gibi bir soruyu aradım, sonra aynı soruyu gönderdim, sonra bu soruyu, göz atarken ve insanların benimkilere cevap vermesini beklerken buldum. Sanırım kendi sorumu mükerrer olarak kapatmak için oy kullanmalıyım. ;-)
Joe Carnahan

Yanıtlar:


147

Yukarıdaki cevaplarda bu olasılığı görmedim, yani:

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)

Düzenle:

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

Bir cazibe gibi :-).


18
Güzel! Sadece bir elemanın kaldırıldığını netleştirmek için listeye 2 tane daha eklerdim.
Frank S. Thomas

40

filterNotYöntemi kullanabilirsin .

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

22
Bu, "test" e eşit olan tüm öğeleri kaldıracaktır
isteneni

1
Aslında tam olarak ihtiyacın olanı yapacak. Listedeki "test" e eşit olmayanlar dışındaki tüm öğeleri döndürür. FilterNot
btbvoy

14
Asıl soru, TEK bir örneğin nasıl kaldırılacağıydı. Tüm örnekler değil.
ty1824

@ Søren Mathiasen val data = Seq ("test", "a") gibi sekans gibi birden fazla öğeyi filtrelemek istersem, nasıl yapılır?
BdEngineer

18

Bunu deneyebilirsin:

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)

Ve yöntem olarak:

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

3
Bunun left ::: right.drop(1)if ifadesinden daha kısa olduğunu belirtmekte fayda var isEmpty.
Rex Kerr

2
Teşekkürler, .tail yerine .drop (1) 'i tercih etmeniz gereken herhangi bir durum var mı, yoksa tam tersi mi?
Gavilan Comun

8
@James Petry - Eğer ararsanız tailboş listesinde bir durum almak: scala> List().tail java.lang.UnsupportedOperationException: tail of empty list. drop(1)boş bir listede ise boş bir liste döndürür.
Frank S. Thomas

3
tailliste boşsa (yani, yoksa head) bir istisna atar . drop(1)boş bir listede başka bir boş liste görüntülenir.
Rex Kerr

8

Ne yazık ki, koleksiyon hiyerarşisi ile bir karmaşa biraz kendisini var -üzerinde List. Çünkü ArrayBufferumduğunuz gibi çalışıyor:

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

ama ne yazık ki, Listbir filterNot-style uygulamasıyla sonuçlandı ve bu nedenle "yanlış şeyi" yapıyor ve size bir kullanımdan filterNotkaldırma uyarısı veriyor (aslında öyle olduğu için yeterince mantıklı ):

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)

Muhtemelen yapılacak en kolay şey, bunu Listdoğru yapan bir koleksiyona dönüştürmek ve sonra tekrar geri dönüştürmek:

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

Alternatif olarak, sahip olduğunuz kodun mantığını koruyabilir ancak stili daha deyimsel hale getirebilirsiniz:

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)

removeInt(5,List(1,2,6,4,5,3,6,4,6,5,1))verim List(4, 6, 2, 1, 3, 6, 4, 6, 5, 1). Sanırım istediğin bu değil.
Ken Bloom

@Ken Bloom - Gerçekten. Yeterince düşünmeden kopyaladığım orijinal algoritmada bir hata. Şimdi düzeltildi.
Rex Kerr

Benim özel durumumda sıra önemli olmadığından, soru spesifikasyonunda daha fazla eksiklik var. Yine de sipariş koruyan sürümü görmek güzel, teşekkürler.
Gavilan Comun

@Rex: 'filtre "yanlış şey değil" derken neyi kastediyorsunuz? Tüm olayları ortadan kaldırdığını mı? Ve neden bir kullanımdan kaldırma uyarısı veriyor? Teşekkürler
teo

1
@teo - Tüm olayları kaldırır (ki burada istenen şey bu değildir) ve muhtemelen bozuk olduğu için (veya belki de istenen davranış belirsizdir - her iki durumda da 2.9'da kullanımdan kaldırıldı ve 2.10'da gitti).
Rex Kerr

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

2

Ne dersin

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

Görürseniz return, bir sorun var.


1
Bu onun istediği şeyi yapmaz, bu sadece ilk örneğini kaldırmaktırc
Ken Bloom

1
Bu, tüm kartları kaldıracaktır c, ancak yalnızca ilk önce kaldırılmalıdır.
tenshi

Soruları daha dikkatli okumalıyım! cevabımı düzelttim.
Eugene Yokota

"Dönüş görürseniz, bir sorun var" için +1. Bu başlı başına çok önemli bir "deyimsel Scala" dersidir.
Joe Carnahan

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

1

Olası çözümlerden biri olarak, ilk uygun öğenin dizinini bulabilir ve ardından bu dizinden öğeyi kaldırabilirsiniz:

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

spanAynı şeyi yapmak için kullanarak cevabımı görün .
Ken Bloom

0

Bunun bir katlama kullanarak nasıl yapılacağına dair başka bir düşünce:

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

0

Genel Kuyruk Özyineleme Çözümü:

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

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

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

    }

}

Bunun soruyu nasıl yanıtladığına dair biraz açıklama (yorumlar, açıklama) ekleyebilir misiniz?
rjp

4
1. Bu soru 5 yıl önce sorulmuş ve cevaplanmıştır. 2. OP "deyimsel" Scala istedi. 2 vars ve bir whiledöngü kullanmak deyimsel Scala değildir.
jwvh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.