What can blow out my laptop in tail recursion function? - scala

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.

Related

How to remake function that group list to simple recursion?

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

Why the first element missing when using the function span?

The function below generates a List
def pack[T](xs: List[T]): List[List[T]] = xs match {
case Nil => Nil
case x::xs =>
val (first, rest) = xs span(y => y==x)
first::pack(rest)
}
When applying pack on a list
val lis4 = List("a", "a", "a", "b", "c", "c", "a")
I get a result
res3: List[List[String]] = List(List(a, a), List(), List(c), List())
However, according to the course given by Martin Odersky on coursera,
it should generate a result
Can anyone tell me what's wrong?
You're shadowing xs defined at the method level with the local binding of xs inside the pattern match. Notice in Oderskys example, the local pattern match bind is called xs1:
def pack[T](xs: List[T]): List[List[T]] = xs match {
case Nil => Nil
case x :: xs1 =>
val (first, rest) = xs span(y => y == x)
first :: pack(rest)
}
To make this even clearer, you can ignore the tail part of the list in the pattern match using _:
def pack[T](xs: List[T]): List[List[T]] = xs match {
case Nil => Nil
case x :: _ =>
val (first, rest) = xs span(y => y == x)
first :: pack(rest)
}
Yields:
scala> pack(List("a", "a", "a", "b", "c", "c", "a"))
res2: List[List[String]] = List(List(a, a, a), List(b), List(c, c), List(a))

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

Passing Empty List of List Argument

I worked on the "Pack Function" exercise from "Functional Programming Principles in Scala".
Put consecutive duplicates into a List[List[T]].
Example
input: List("a", "a", "b", "b", "c", "a")
output: List(List(a, a), List(b, b), List(c), List(a))
Given this function...
def pack[T](xs: List[T]): List[List[T]] = {
def go[T](ys: List[T], acc: List[List[T]]) : List[List[T]] = ys match {
case Nil => acc
case x :: xs_ => val r: List[T] = ys.takeWhile(a => a == x)
go(ys.drop(r.length), acc :+ r)
}
go(xs, List(Nil).filter(_ != Nil)) // *** line in question ***
}
Is there a better way to pass in a List[List[T]] where the inner list is empty?
If I didn't have the filter there, the head of pack(...)'s result would be List().
Why not just...........:
go(xs, Nil)
BTW, there is my solution for this problem:
def pack[T](xs: List[T],
acc: List[List[T]] = Nil): List[List[T]] =
(xs, acc) match {
case (Nil, _) => acc
case (i:+last, (h::t1)::t2) if last == h => pack(i, (last::h::t1)::t2)
case (i:+last, acc0) => pack(i, List(last)::acc0)
}
And here is another solution:
def pack[T](xs: List[T]): List[List[T]] = xs match {
case Nil => Nil
case x::rs => pack(rs) match {
case (h#`x`::_)::t => (x::h)::t
case t => List(x)::t
}
}

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)