Scala เป็นวิธีที่ใช้สำนวนในการ "ลบ" องค์ประกอบหนึ่งออกจากรายการที่ไม่เปลี่ยนรูปได้อย่างไร


86

ฉันมีรายการซึ่งอาจมีองค์ประกอบที่จะเปรียบเทียบว่าเท่ากัน ฉันต้องการรายการที่คล้ายกัน แต่มีการลบองค์ประกอบหนึ่งรายการ ดังนั้นจาก (A, B, C, B, D) ฉันต้องการจะสามารถ "ลบ" B เพียงตัวเดียวเพื่อให้ได้เช่น (A, C, B, D) ลำดับขององค์ประกอบในผลลัพธ์ไม่สำคัญ

ฉันมีรหัสการทำงานที่เขียนด้วยวิธีที่ได้รับแรงบันดาลใจจาก Lisp ใน Scala มีวิธีสำนวนมากกว่านี้ไหม?

บริบทคือเกมไพ่ที่มีการเล่นไพ่มาตรฐานสองสำรับดังนั้นอาจมีไพ่ที่ซ้ำกัน แต่ยังคงเล่นทีละใบ

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

เพิ่มหมายเหตุว่าลำดับของรายการผลลัพธ์ไม่สำคัญในกรณีนี้
Gavilan Comun

ดังนั้นList[Card]ในคำถามนี้คือมือของผู้เล่น?
Ken Bloom

@ เคนบลูมใช่นั่นคือมือของผู้เล่น
Gavilan Comun

คุณรู้ไหมว่าฉันค้นหาคำถามแบบนี้มาระยะหนึ่งแล้วโพสต์คำถามเดียวกันจากนั้นก็พบคำถามนี้ในขณะที่ฉันกำลังเรียกดูและรอให้คนอื่นตอบคำถามของฉัน เดาว่าฉันควรโหวตเพื่อปิดคำถามของตัวเองตอนนี้ว่าซ้ำกัน ;-)
Joe Carnahan

คำถามนี้สำหรับ Clojure: stackoverflow.com/questions/7662447/…
Gavilan Comun

คำตอบ:


147

ฉันไม่เห็นความเป็นไปได้นี้ในคำตอบด้านบนดังนั้น:

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)

แก้ไข:

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

เหมือน :-) เสน่ห์.


18
ดี! ฉันจะเพิ่มอีก 2 รายการในรายการเพื่อให้ชัดเจนว่ามีเพียงองค์ประกอบเดียวเท่านั้นที่ถูกลบออก
Frank

40

คุณสามารถใช้filterNotวิธีนี้

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

22
สิ่งนี้จะลบองค์ประกอบทั้งหมดที่เท่ากับ "การทดสอบ" - ไม่ใช่สิ่งที่ขอ;)
yǝsʞǝla

1
จริงๆแล้วมันจะทำสิ่งที่คุณต้องการ มันจะส่งคืนองค์ประกอบทั้งหมดจากรายการยกเว้นองค์ประกอบที่ไม่เท่ากับ "ทดสอบ" สนใจว่ามันใช้filterNot
btbvoy

14
คำถามเดิมคือวิธีลบอินสแตนซ์ SINGLE ไม่ใช่ทุกอินสแตนซ์
ty1824

@ Søren Mathiasen ถ้าฉันต้องการกรองหลายองค์ประกอบเช่นลำดับเช่น val data = Seq ("test", "a") ต้องทำอย่างไร
BdEngineer

18

คุณสามารถลองสิ่งนี้:

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)

และเป็นวิธีการ:

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

3
มันน่าสังเกตว่าสั้นกว่าคำสั่งถ้ามีleft ::: right.drop(1) isEmpty
Rex Kerr

2
ขอบคุณมีกรณีใดบ้างที่จะชอบ. drop (1) มากกว่า. tail หรือในทางกลับกัน?
Gavilan Comun

8
@James Petry - ถ้าคุณโทรในรายการที่ว่างเปล่าที่คุณได้รับข้อยกเว้น:tail ในรายการว่าง แต่จะส่งกลับรายการว่าง scala> List().tail java.lang.UnsupportedOperationException: tail of empty listdrop(1)
Frank S. Thomas

3
tailแสดงข้อยกเว้นหากรายการว่างเปล่า (กล่าวคือไม่มีhead) drop(1)ในรายการว่างเพียงแค่ให้รายการว่างอีกรายการ
Rex Kerr

8

แต่น่าเสียดายที่ลำดับชั้นของคอลเลกชันมีตัวเองเป็นบิตของระเบียบที่มีอยู่บน- Listเพื่อให้ArrayBufferได้ผลเช่นเดียวกับที่คุณคาดหวัง:

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

แต่น่าเศร้าที่Listลงเอยด้วยการfilterNotใช้งานสไตล์จึง "ผิด" และส่งคำเตือนการเลิกใช้งานมาที่คุณ (สมเหตุสมผลเพียงพอเนื่องจากเป็นสิ่งที่เกิดขึ้นจริงfilterNot):

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)

ดังนั้นสิ่งที่ง่ายที่สุดในการทำคือแปลงListเป็นคอลเล็กชันที่ทำสิ่งนี้ได้ถูกต้องแล้วแปลงกลับอีกครั้ง:

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

หรือคุณสามารถใช้ตรรกะของรหัสที่คุณมี แต่ทำให้สไตล์เป็นสำนวนมากขึ้น:

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))List(4, 6, 2, 1, 3, 6, 4, 6, 5, 1)อัตราผลตอบแทน ฉันคิดว่านี่ไม่ใช่สิ่งที่คุณต้องการ
Ken Bloom

@ เคนบลูม - แน่นอน มันเป็นข้อผิดพลาดในอัลกอริทึมดั้งเดิมซึ่งฉันคัดลอกโดยไม่คิดว่าเพียงพอ แก้ไขแล้ว
Rex Kerr

การละเว้นเพิ่มเติมในข้อกำหนดของคำถามเนื่องจากคำสั่งซื้อไม่สำคัญในกรณีเฉพาะของฉัน ดีที่ได้เห็นเวอร์ชันที่รักษาคำสั่งซื้อขอบคุณ
Gavilan Comun

@Rex: คุณหมายถึงอะไรโดย "filterNot" ไม่ "ผิด" " นั่นเป็นการลบเหตุการณ์ทั้งหมดออกไป? และเหตุใดจึงส่งคำเตือนการเลิกใช้งาน ขอบคุณ
teo

1
@teo - มันจะลบเหตุการณ์ที่เกิดขึ้นทั้งหมด (ซึ่งไม่ใช่สิ่งที่ต้องการที่นี่) และเลิกใช้แล้วเนื่องจากมีเนื้อหาแตก (หรือพฤติกรรมที่ต้องการอาจไม่ชัดเจน - ไม่ว่าจะด้วยวิธีใดก็เลิกใช้ใน 2.9 และไปใน 2.10)
Rex Kerr


2

เกี่ยวกับ

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

ถ้าคุณเห็นreturnมีบางอย่างผิดปกติ


1
สิ่งนี้ไม่ได้ทำในสิ่งที่เขาต้องการซึ่งก็คือการลบเพียงตัวอย่างแรกของc
Ken Bloom

1
การดำเนินการนี้จะนำการ์ดทั้งหมดออกcแต่ควรนำออกก่อนเท่านั้น
tenshi

ฉันควรอ่านคำถามอย่างละเอียด! แก้ไขคำตอบของฉัน
Eugene Yokota

+1 สำหรับ "หากคุณเห็นการส่งคืนมีบางอย่างผิดปกติ" นั่นเป็นบทเรียน "สำนวน Scala" ที่สำคัญมากด้วยตัวมันเอง
Joe Carnahan


1

ในฐานะที่เป็นวิธีแก้ปัญหาที่เป็นไปได้คุณสามารถค้นหาดัชนีขององค์ประกอบแรกที่เหมาะสมจากนั้นลบองค์ประกอบที่ดัชนีนี้:

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

ดูคำตอบของฉันโดยใช้spanเพื่อทำสิ่งเดียวกัน
Ken Bloom

0

อีกหนึ่งความคิดเกี่ยวกับวิธีการใช้การพับ:

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

0

โซลูชันการเรียกซ้ำหางทั่วไป:

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


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

    }

}

คุณช่วยเพิ่มคำอธิบาย (ความคิดเห็นคำอธิบาย) เกี่ยวกับวิธีการตอบคำถามนี้
rjp

4
1. คำถามนี้ถูกถามและตอบเมื่อ 5 ปีที่แล้ว 2. OP ขอ "สำนวน" Scala การใช้ 2 varวินาทีและการwhileวนซ้ำไม่ใช่สำนวน Scala
jwvh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.