How to remake function that group list to simple recursion? - scala

I write function that group list elements by index, with odd index in first list, even in second. But I don not know how to make it with simple recursion and don not get type mismatch.
Here is the code:
// Simple recursion
def group1(list: List[Int]): (List[Int], List[Int]) = list match {
case Nil => (Nil, Nil)
case head :: Nil => (List(head), Nil)
case head :: tail => // how can I make this case?
}
group1(List(2, 6, 7, 9, 0, 4, 1))
// Tail recursion
def group2(list: List[Int]): (List[Int], List[Int]) = {
def group2Helper(list: List[Int], listA: List[Int], listB: List[Int]): (List[Int], List[Int]) = list match {
case Nil => (listA.reverse, listB.reverse)
case head :: Nil => ((head :: listA).reverse, listB.reverse)
case head :: headNext :: tail => group2Helper(tail, head :: listA, headNext :: listB)
}
group2Helper(list, Nil, Nil)
}
group2(List(2, 6, 7, 9, 0, 4, 1))

You have to invoke the next recursion, unpack the result tuple, pre-pend each head element to the proper List, and repackage the new result tuple.
def group1(list: List[Int]) :(List[Int], List[Int]) = list match {
case Nil => (Nil, Nil)
case head :: Nil => (List(head), Nil)
case hdA :: hdB :: tail => val (lstA, lstB) = group1(tail)
(hdA :: lstA, hdB :: lstB)
}

Related

Pattern Matching List[Any] with nested lists Scala?

How can i achieve this? Where xs is a List[Any].
def flatten(xs: List[Any]): List[Any] = {
xs match {
case x: List[Any] :: t => flatten(x) ::: flatten(t)
case x :: t => x :: flatten(t)
case Nil => Nil
}
}
The first case does not work properly. For some reason I cannot give a type to the head of the list x.
As #Luis mentioned, this is really bad idea to use List[Any] but you still want to write flatten, then using reflection you can do like this:
val xs: List[Any] = List(List(1, 2), 3, 4)
def flatten(xs: List[Any]): List[Any] = {
xs match {
case x :: t if x.isInstanceOf[List[_]] => flatten(x.asInstanceOf[List[Any]]) ::: flatten(t)
case x :: t => x :: flatten(t)
case Nil => Nil
}
}
println(flatten(xs)) // List(1, 2, 3, 4)

What can blow out my laptop in tail recursion function?

I need to make function that group list into two lists by odd and even numbers of index, start from 1, not 0. I have a problem with infinity recursion, as I think, because time of doing second function is too much and my laptop is going to take the mars.
First function with simple recursion works fine, but the second, merge2, with tail recursion is blowing out my computer.
Here is the code:
// Simple recursion
def merge1(listA: List[String], listB: List[String]): List[String] = (listA, listB) match {
case (Nil, Nil) => Nil
case (head1 :: tail1, Nil) => head1 :: merge1(tail1, Nil)
case (Nil, head2 :: tail2) => head2 :: merge1(Nil, tail2)
case (head1 :: tail1, head2 :: tail2) => head1 + head2 :: merge1(tail1, tail2)
}
merge1 (List("a", "b", "c", "d"), List("e", "f", "g", "h", "i", "j"));
// Tail recursion
def merge2 (listA1: List[String], listB1: List[String]): List[String] = {
def merge2Helper(listA: List[String], listB: List[String], listACC: List[String]): List[String] =
(listA, listB) match {
case (Nil, Nil) => listACC
case (head1 :: tail1, Nil) => merge2Helper(tail1, listB, listACC ::: List(head1))
case (Nil, head2 :: tail2) => merge2Helper(tail2, listB, listACC ::: List(head2))
case (head1 :: tail1, head2 :: tail2) => merge2Helper(tail1, tail2, listACC ::: List(head1 + head2))
}
merge2Helper(listA1, listB1, Nil)
}
merge2 (List("a", "b", "c", "d"), List("e", "f", "g", "h", "i", "j"));
The 3rd case is wrong, it is passing the same list again that has just been processed.
It should be like this:
#tailrec def merge2Helper(listA: List[String], listB: List[String], listACC: List[String]): List[String] =
(listA, listB) match {
case (Nil, Nil) => listACC
case (head1 :: tail1, Nil) => merge2Helper(tail1, listB, listACC ::: List(head1))
case (Nil, head2 :: tail2) => merge2Helper(listA, tail2, listACC ::: List(head2))
case (head1 :: tail1, head2 :: tail2) => merge2Helper(tail1, tail2, listACC ::: List(head1 + head2))
}
You should also add the #tailrec annotation to hint the compiler you expect the function to be tail recursive (and fail if it is not), to ensure you don't end up in a stack overflow with long lists.

Remove Duplicates from the List recursively

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

Case with no return value

I have the following unit test:
FlattenArray.flatten(
List(0, 2, List(List(2, 3), 8, List(List(100)), null, List(List(null))), -2))
should be(List(0, 2, 2, 3, 8, 100, -2))
With my implementation as follow:
object FlattenArray {
def flatten(list: List[Any]): List[Any] = {
list match {
case Nil => Nil
case (x: List[Any]) :: tail => flatten(x) ::: flatten(tail)
case x :: tail => x :: flatten(tail)
}
}
}
The test if failing because, on case Nil I should add no value to the flatten list: any suggestion on how to do so?
I could filter out from the flatten list null values: is that the correct implementation?
You can add a special case for null :: tail which returns flatten(tail):
def flatten(list: List[Any]): List[Any] = {
list match {
case Nil => Nil
case null :: tail => flatten(tail)
case (x: List[Any]) :: tail => flatten(x) ::: flatten(tail)
case x :: tail => x :: flatten(tail)
}
}

Scala flatten a List

I want to write a function that flattens a List.
object Flat {
def flatten[T](list: List[T]): List[T] = list match {
case Nil => Nil
case head :: Nil => List(head)
case head :: tail => (head match {
case l: List[T] => flatten(l)
case i => List(i)
}) ::: flatten(tail)
}
}
object Main {
def main(args: Array[String]) = {
println(Flat.flatten(List(List(1, 1), 2, List(3, List(5, 8)))))
}
}
I don't know why it don't work, it returns List(1, 1, 2, List(3, List(5, 8))) but it should be List(1, 1, 2, 3, 5, 8).
Can you give me a hint?
You don't need to nest your match statements. Instead do the matching in place like so:
def flatten(xs: List[Any]): List[Any] = xs match {
case Nil => Nil
case (head: List[_]) :: tail => flatten(head) ++ flatten(tail)
case head :: tail => head :: flatten(tail)
}
My, equivalent to SDJMcHattie's, solution.
def flatten(xs: List[Any]): List[Any] = xs match {
case List() => List()
case (y :: ys) :: yss => flatten(y :: ys) ::: flatten(yss)
case y :: ys => y :: flatten(ys)
}
By delete line 4
case head :: Nil => List(head)
You will get right answer.
Think about the test case
List(List(List(1)))
With line 4 last element in list will not be processed
def flatten(ls: List[Any]): List[Any] = ls flatMap {
case ms: List[_] => flatten(ms)
case e => List(e)
}
If someone does not understand this line of the accepted solution, or did not know that you can annotate a pattern with a type:
case (head: List[_]) :: tail => flatten(head) ++ flatten(tail)
Then look at an equivalent without the type annotation:
case (y :: ys) :: tail => flatten3(y :: ys) ::: flatten3(tail)
case Nil :: tail => flatten3(tail)
So, just for better understanding some alternatives:
def flatten2(xs: List[Any]): List[Any] = xs match {
case x :: xs => x match {
case y :: ys => flatten2(y :: ys) ::: flatten2(xs)
case Nil => flatten2(xs)
case _ => x :: flatten2(xs)
}
case x => x
}
def flatten3(xs: List[Any]): List[Any] = xs match {
case Nil => Nil
case (y :: ys) :: zs => flatten3(y :: ys) ::: flatten3(zs)
case Nil :: ys => flatten3(ys)
case y :: ys => y :: flatten3(ys)
}
val yss = List(List(1,2,3), List(), List(List(1,2,3), List(List(4,5,6))))
flatten2(yss) // res2: List[Any] = List(1, 2, 3, 1, 2, 3, 4, 5, 6)
flatten3(yss) // res2: List[Any] = List(1, 2, 3, 1, 2, 3, 4, 5, 6)
By the way, the second posted answer will do the following, which you probably don't want.
val yss = List(List(1,2,3), List(), List(List(1,2,3), List(List(4,5,6))))
flatten(yss) // res1: List[Any] = List(1, 2, 3, List(), 1, 2, 3, 4, 5, 6)