불변 목록에서 한 요소를 "제거"하는 관용적 스칼라 방식은 무엇입니까?


84

동일한 것으로 비교할 요소를 포함 할 수있는 목록이 있습니다. 비슷한 목록을 원하지만 하나의 요소가 제거되었습니다. 따라서 (A, B, C, B, D)에서 하나의 B 만 "제거"하여 예를 들어 (A, C, B, D)를 얻을 수 있기를 바랍니다. 결과의 요소 순서는 중요하지 않습니다.

저는 Scala에서 Lisp에서 영감을받은 방식으로 작성된 작업 코드를 가지고 있습니다. 이것을 수행하는 더 관용적 인 방법이 있습니까?

컨텍스트는 두 개의 표준 카드 덱이 사용되는 카드 게임이므로 중복 카드가있을 수 있지만 여전히 한 번에 하나씩 플레이됩니다.

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 2011

그래서이 List[Card]질문은 플레이어의 손입니까?
Ken Bloom

@Ken Bloom, 네 맞습니다. 플레이어의 손입니다.
가빌란 Comun

아시다시피, 나는 잠시 동안 이와 같은 질문을 검색 한 다음 동일한 질문을 게시 한 다음 탐색하고 사람들이 내 대답을 기다리는 동안이 질문을 찾았습니다. 지금 내 질문을 중복으로 종료하려면 투표해야한다고 생각합니다. ;-)
Joe Carnahan

답변:


144

위의 답변에서 이러한 가능성을 보지 못 했으므로 다음과 같습니다.

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 S. Thomas

39

filterNot방법을 사용할 수 있습니다 .

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

21
이것은 "테스트"와 같은 모든 요소를 제거합니다 - 요구되는하지 무엇을)
yǝsʞǝla

1
실제로 필요한 작업을 정확히 수행합니다. 목록에서 "test"와 같지 않은 요소를 제외한 모든 요소를 ​​반환합니다. 그것은 사용에주의 filterNot을
btbvoy

14
원래 질문은 단일 인스턴스를 제거하는 방법이었습니다. 모든 경우가 아닙니다.
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
.NET을 사용 left ::: right.drop(1)하는 if 문보다 짧다는 점 은 주목할 가치가 isEmpty있습니다.
Rex Kerr 2011

2
감사합니다. .tail보다 .drop (1)을 선호하거나 그 반대의 경우가 있습니까?
Gavilan Comun 2011

8
@James Petry- tail빈 목록에서 전화를 걸면 예외가 발생 scala> List().tail java.lang.UnsupportedOperationException: tail of empty list합니다.. drop(1)그러나 빈 목록에서는 빈 목록을 반환합니다.
Frank S. Thomas

3
tail목록이 비어있는 경우 (즉, 없음 head) 예외가 발생합니다 . drop(1)빈 목록에서 또 다른 빈 목록을 생성합니다.
Rex Kerr 2011

8

불행히도 컬렉션 계층 구조는 -on 에서 약간 엉망이 되었습니다 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 2011

@Ken Bloom-실제로. 충분히 생각하지 않고 복사 한 원래 알고리즘의 오류입니다. 지금 수정되었습니다.
Rex Kerr 2011

내 특정 경우에는 순서가 중요하지 않기 때문에 질문 사양에 더 많은 누락이 있습니다. 그래도 주문 보존 버전을 보니 반갑습니다. 감사합니다.
Gavilan Comun 2011

@Rex : '필터가 "잘못된 것"이 아니라는 것은 무엇을 의미합니까? 모든 발생을 제거하고 있습니까? 그리고 왜 사용 중단 경고를 표시합니까? 감사합니다
teo

1
@teo-모든 발생을 제거하고 (여기에서는 원하는 것이 아님) 의심 할 여지없이 손상 되었기 때문에 더 이상 사용되지 않습니다 (또는 원하는 동작이 명확하지 않음-어느 쪽이든 2.9에서 더 이상 사용되지 않고 2.10에서 사라짐).
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

어때

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
켄 꽃을

1
이렇게하면 모든 카드가 제거 c되지만 먼저 제거해야합니다.
tenshi

질문을 더주의 깊게 읽어야합니다! 내 대답을 수정했습니다.
Eugene Yokota 2011

+1 "반환이 보이면 문제가있는 것입니다." 그것은 그 자체로 매우 중요한 "관용적 스칼라"교훈입니다.
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

한 가지 가능한 솔루션으로 첫 번째 적합한 요소의 색인을 찾은 다음이 색인에서 요소를 제거 할 수 있습니다.

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

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

    }

}

질문에 대한 답변에 대한 설명 (댓글, 설명)을 추가해 주시겠습니까?
rjp

4
1.이 질문은 5 년 전에 질문 및 답변되었습니다. 2. OP는 "idiomatic"Scala를 요구했습니다. 2 varwhile루프를 사용하는 것은 관용적 인 스칼라가 아닙니다.
jwvh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.