问题
I want to remove duplicates from the list recursively using pattern matching with Scala
here is my input
val xs = List(1,2,3,4,6,3,2,7,9,4)
Tried code:
def removeDuplicates(xs : List[Int]) : List[Int] = xs match {
case Nil =>Nil
case x::ys => {
if(ys.contains(x)){
removeDuplicates(ys)
} else {
}
/// ???
}
}
I was stuck at the question mark, how to appened my result to the mutable list and return it.
Thank you.
回答1:
You're close:
def removeDuplicates(xs : List[Int]) : List[Int] = xs match {
case Nil => Nil
case x::ys => if (ys.contains (x)) removeDuplicates (ys) else
x :: removeDuplicates (ys)
}
scala> removeDuplicates (List (1,2,3,4,6,3,2,7,9,4))
res143: List[Int] = List(1, 6, 3, 2, 7, 9, 4)
While this is a brief solution, it isn't tail recursive and therefore vulnerable for stackoverflows - whereas Jean Logearts solution solves the problem.
Here is an alternative solution with an inner function, tailrecursive too:
def removeDuplicates (xsOuter : List[Int]) : List[Int] = {
@annotation.tailrec
def removeDuplicates (xs: List[Int], collected: List[Int]) : List[Int] = xs match {
case Nil => collected
case x :: ys => if (collected.contains (x)) removeDuplicates (ys, collected) else
removeDuplicates (ys, x :: collected)
}
removeDuplicates (xsOuter, Nil)
}
scala> removeDuplicates (List (1,2,3,4,6,3,2,7,9,4))
res151: List[Int] = List(9, 7, 6, 4, 3, 2, 1)
with a bias to the first elements, but with the result reversed, which can be easily corrected by returning collected.reverse in the Nil case, if it is important.
The outer function serves the job to provide a simple, one-argument interface to the user, so he doesn't need to provide an empty List.
Note, that the solution is crying for a type annotation, since it doesn't depend at all on the List elements being of type Int:
scala> def removeDuplicates [A] (xsOuter : List[A]) : List[A] = {
|
| @annotation.tailrec
| def removeDuplicates (xs: List[A], collected: List[A]) : List[A] = xs match {
| case Nil => collected
| case x :: ys => if (collected.contains (x)) removeDuplicates (ys, collected) else
| removeDuplicates (ys, x :: collected)
| }
|
| removeDuplicates (xsOuter, Nil)
| }
removeDuplicates: [A](xsOuter: List[A])List[A]
scala> removeDuplicates (List (1,2,3,4,6,3,2,7,9,4))
res152: List[Int] = List(9, 7, 6, 4, 3, 2, 1)
回答2:
You need to keep track of the current state: already seen elements using a set (for fast lookup), and the new list being constructed:
@tailrec
def removeDuplicatesRec(
remaining: List[Int],
seen: Set[Int],
acc: List[Int]
): List[Int] = remaining match {
case Nil => acc
case head :: tail =>
if (!seen.contains(head)) removeDuplicatesRec(tail, seen + head, acc :+ head)
else removeDuplicatesRec(tail, seen, acc)
}
def removeDuplicates(xs: List[Int]): List[Int] =
removeDuplicatesRec(xs, Set.empty, List.empty)
回答3:
Here's a classic approach using an inner function and tail recursion. The tail recursion may not be necessary for small lists but it is easier for me to reason about.
def removeDuplicates(xs : List[Int]) : List[Int] = {
@scala.annotation.tailrec
def accumulator(xs: List[Int], acc: List[Int]):List[Int] = xs match {
case Nil => acc
case h::t if(!acc.contains(h)) => accumulator(t, h :: acc)
case h::t if(acc.contains(h)) => accumulator(t, acc)
}
accumulator(xs, List[Int]())
}
scala> removeDuplicates(List(1,2,3,4,6,3,2,7,9,4))
res16: List[Int] = List(9, 7, 6, 4, 3, 2, 1)
Of course, distinct is the preferred way to do this but this is a good exercise. distinct can be used to verify your solution though.
scala> List(1,2,3,4,6,3,2,7,9,4).distinct == removeDuplicates(List(1,2,3,4,6,3,2,7,9,4)).sorted
res21: Boolean = true
来源:https://stackoverflow.com/questions/49929630/remove-duplicates-from-the-list-recursively