不変のリストから1つの要素を「削除」する慣用的なScalaの方法は何ですか?


84

私はリストを持っています、それは等しいと比較する要素を含むかもしれません。同様のリストが欲しいのですが、1つの要素が削除されています。したがって、(A、B、C、B、D)から、たとえば(A、C、B、D)を取得するために1つのBだけを「削除」できるようにしたいと思います。結果の要素の順序は重要ではありません。

私はScalaでLispに触発された方法で書かれた作業コードを持っています。これを行うためのより慣用的な方法はありますか?

コンテキストは、標準カードの2つのデッキがプレイされているカードゲームであるため、重複するカードが存在する可能性がありますが、一度に1つずつプレイされます。

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]この質問ではプレイヤーの手ですか?
ケンブルーム

@ケンブルーム、はい、そうです、プレイヤーの手です。
Gavilan Comun 2011

ご存知のように、私はしばらくこのような質問を検索し、同じ質問を投稿し、閲覧して人々が私の答えを待つ間にこれを見つけました。自分の質問を重複して閉じるために投票する必要があると思います。;-)
Joe Carnahan

Clojureのためのこの質問:stackoverflow.com/questions/7662447/...
Gavilan Comun

回答:


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を追加して、1つの要素のみが削除されることを明確にします。
フランクS.トーマス

39

このfilterNot方法を使用できます。

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

21
これにより、「テスト」に等しいすべての要素が削除されます
yǝsʞǝla2013年

1
実際、それはあなたが必要とすることを正確に行います。「test」と等しくない要素を除いて、リストからすべての要素を返します。filterNotを
btbvoy

14
元の質問は、SINGLEインスタンスを削除する方法でした。すべてのインスタンスではありません。
ty1824 2015年

@SørenMathiasenvaldata= 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)たifステートメントよりも短いことに注意してくださいisEmpty
レックスカー

2
おかげで、.tailよりも.drop(1)を好む、またはその逆の状況はありますか?
Gavilan Comun 2011

8
@James Petry-tail空のリストを呼び出すと、例外が発生します:scala> List().tail java.lang.UnsupportedOperationException: tail of empty listdrop(1)ただし、空のリストでは空のリストを返します。
フランクS.トーマス

3
tailリストが空の場合(つまり、がない場合head)は例外をスローします。 drop(1)空のリストでは、別の空のリストが生成されます。
レックスカー

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)ます。これはあなたが望んでいたことではないと思います。
ケンブルーム

@ケンブルーム-確かに。これは、私が十分に考えずにコピーした元のアルゴリズムのエラーです。修正されました。
レックスカー

私の特定のケースでは順序は重要ではないため、質問の仕様をさらに省略しています。でも、注文保存バージョンを見るのは良いことです、ありがとう。
Gavilan Comun 2011

@Rex:「filterNotは「間違ったこと」をしない」とはどういう意味ですか?それはすべての発生を削除しているということですか?そして、なぜそれは非推奨の警告をスローするのですか?ありがとう
teo 2013年

1
@ teo-すべてのオカレンスを削除し(これはここで望まれることではありません)、おそらく壊れているため(またはおそらく望ましい動作が不明です-いずれにしても、2.9で非推奨になり、2.10で廃止されます)非推奨になります。
レックスカー

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
Ken Bloom

1
これによりすべてのカードが削除cされますが、最初に削除する必要があります。
tenshi 2011

私は質問をもっと注意深く読むべきです!私の答えを訂正しました。
Eugene Yokota

「リターンが表示された場合は、何か問題があります。」の+1 それ自体が非常に重要な「慣用的なScala」のレッスンです。
ジョー・カーナハン2011年

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

考えられる解決策の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同じことをするために使用する私の答えを参照してください。
ケンブルーム

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 2016

4
1.この質問は、5年前に質問され、回答されました。2.OPは「慣用的な」Scalaを要求しました。2var秒とwhileループを使用することは慣用的なScalaではありません。
jwvh 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.