I am having problems with this function. I need to process more than 1 million records, but this crashes. Looks like just works with thousands of records and throws a StackOverflowError for a larger list. Any suggestions?
def split(list: List[(Pair, Boolean)]): List[List[Pair]] = list match {
case Nil => Nil
case head :: tail => {
val (l1, l2) = tail.span(!_._2)
(head :: l1).map(_._1) :: split(l2)
}
}
Your program will throw a StackOverflow exception:
Exception in thread "main" java.lang.StackOverflowError
at scala.collection.immutable.List.map(List.scala:283)
The reason is very simple because your method is not tail-recursive
If you annotate it with #tailrec, it won't compile:
#tailrec
def split(list: List[(Pair, Boolean)]): List[List[Pair]] = list match {
case Nil => Nil
case head :: tail => {
val (l1, l2) = tail.span(!_._2)
(head :: l1).map(_._1) :: split(l2)
}
}
The solution is to make your recusion tailrec, or use some kind of loop instead
You could try something like this:
#tailrec
def split(list: List[(Pair, Boolean)], accumulator: List[List[Pair]] = List[List[Pair]]()): List[List[Pair]] = list match {
case Nil => accumulator
case head :: tail => {
val (l1, l2) = tail.span(!_._2)
split(l2, (head :: l1).map(_._1)::accumulator)
}
}
Related
I am trying to write a recursive function in scala that takes in a list of Strings and returns a list with alternating elements from original list:
For example:
List a = {"a","b","c"}
List b = {"a","c"}
the head should always be included.
def removeAlt(list:List[String], str:String):List[String]=lst match{
case Nil=> List()
case => head::tail
if(head == true)
removeAlternating(list,head)
else
head::removeAlternating(list,head)
I get a stack overflow error.
I understand that the code is incorrect but I am trying to understand the logic on how to accomplish this with only recursion and no built in classes.
def remove[A](xs:List[A]):List[A] = xs match {
case Nil => Nil
case x::Nil => List(x)
case x::y::t => x :: remove(t)
}
if the list is empty, return a empty list.
If we're at the last element of the list, return that.
Otherwise, there must be two or more elements. Add to the first element the alternate elements of the rest of the list (and omit the second element)
Great exercise. This is what I came up with. It is not super optimized or anything:
def altList[T](rest: List[T], skip: Boolean): List[T] = {
rest match {
case Nil => Nil
case a :: tail if skip == false => a :: altList(tail, true)
case a :: tail if skip == true => altList(tail, false)
}
}
A bit shorter alternative:
def remove[A](xs:List[A]):List[A] = xs match {
case x::_::t => x :: remove(t)
case _ => xs
}
UPDATE
What is not so good with the above approach is eventual stack overflow for long lists, so I would suggest tail recursion:
import scala.annotation.tailrec
def remove[A](xs:List[A]):List[A] = {
#tailrec
def remove_r(xs:List[A], ys:List[A]):List[A] = xs match {
case x::_::t => remove_r(t, x::ys)
case _ => xs ++ ys
}
remove_r(xs, Nil).reverse
}
I have the following recursive function which I want to use tail recursion on it. But compiler complains about my implementation with this error:Error:(79, 7) could not optimize #tailrec annotated method loop: it contains a recursive call not in tail position
n match {
^
is it because of for loop that it assumes it's not in tail position?
def dsl[N,E](qNodes:QNodeLike[N,E]*) = {
val markers = scala.collection.mutable.Map.empty[String, N]
#tailrec
def loop(n:QNodeLike[N,E]):Unit = {
n match {
case QNode(head, kids:Seq[HalfEdgeLike[E,N]]) => {
for(kid <- kids){
kid match {
case EmptyHalfEdge() =>
case HalfEdge(e, n) => loop(n)
}
}
}
case QNodeMarker(head, marker, kids:Seq[HalfEdgeLike[E,N]]) => {
markers.update(marker,head)
for(kid <- kids){
kid match {
case EmptyHalfEdge() =>
case HalfEdge(e, n) => loop(n)
}
}
}
}
}
loop(qNodes.head)
}
Yes, that's right. To make it tail recursive, you should use an explicit accumulator which is passed into the recursion.
However, unless you have very deep and narrow trees, you are unlikely to need tail recursive optimization, as the run time will grow very large well before you end up with a stack overflow.
Here's a rough idea of how to make it tail optimized:
#tailrec
def loop(n:List[QNodeLike[N,E]]):Unit = {
n match {
case QNode(head, kids:Seq[HalfEdgeLike[E,N]]) :: rem => {
kids match {
case Nil =>
case EmptyHalfEdge() :: rem2 => loop(rem2 ::: rem)
case HalfEdge(e, n) :: rem2 => loop(n :: rem2 ::: rem)
}
}
case QNodeMarker(head, marker, kids:Seq[HalfEdgeLike[E,N]]) :: rem => {
markers.update(marker,head)
kids match {
case Nil =>
case EmptyHalfEdge() :: rem2 => loop(rem2 ::: rem)
case HalfEdge(e, n) :: rem2 => loop(n :: rem2 ::: rem)
}
}
case Nil =>
}
}
Yes it is because of the loop. The result of a tailrec funtion has to be the result of the recursive call. In your case the result is the result of the for statement.
When running this piece of code:
object P01 {
#annotation.tailrec
final def lastRecursive[A] (ls:List[A]):A = {
def lr[A] (l:List[A]):A = l match {
case h :: Nil => h
case _ :: tail => lr(tail)
case _ => throw new NoSuchElementException
}
lr(ls)
}
}
P01.lastRecursive(List(1,2,3))
,in scala 2.10.2 REPL, I get the following error:
scala> :9: error: #tailrec annotated method contains no recursive calls
final def lastRecursive[A] (ls:List[A]):A = {
^
Please help, I don't understand what am I doing wrong.
lastRecursive isn't tail recursive but lr is. This worked for me:
object P01 {
final def lastRecursive[A] (ls:List[A]):A = {
#annotation.tailrec
def lr[A] (l:List[A]):A = l match {
case h :: Nil => h
case _ :: tail => lr(tail)
case _ => throw new NoSuchElementException
}
lr(ls)
}
}
I have a list of text lines, and want to treat any lines ending with '\' as continuing to the next line, i.e. to merge them. The recursive code below does it, but there must be some clever way, similar to map, filter and all?
reduceLeft is close but it only produces a single result, not a modified (and potentially shorter) new list.
Also suggestions on making the code below leaner are welcome.
object TestX extends App {
// Merge lines together if the former ends with '\'.
//
private def mergeLines( list: List[String] ): List[String] = {
def merge( head: String, tail: List[String] ): List[String] = {
if (head.endsWith("\\")) {
val head2= head.dropRight(1)
if (tail.isEmpty) {
head2 :: Nil // or throw an exception on bad input
} else {
merge( head2 + tail.head, tail.tail )
}
} else {
if (tail.isEmpty)
head :: Nil
else
head :: merge( tail.head, tail.tail ) // note: cannot tailrec this
}
}
if (list.isEmpty) {
list
} else {
merge( list.head, list.tail )
}
}
val list = "These two \\" :: "should be joined" :: "but not this." :: Nil
val list2 = mergeLines(list) // any standard easy way to do this? 'list.mergeIf( _.endsWith('\\') )'
println( list2 )
assert( list2.size == 2 )
}
You can write it using foldLeft:
List("a\\", "b", "c").foldLeft(List.empty[String])((xs, x) => xs match {
case Nil => x :: Nil
case _ => if (xs.head.endsWith("\\")) (xs.head.dropRight(1) + x) :: xs.tail else x :: xs
}).reverse
It's probably not the most efficient way (fine for small list, but not for huge) as it use an immutable data structure, a more efficient approach would use a mutable List.
Here are a few tricks that you could use:
#annotation.tailrec
def mergeLines(xs: List[String], out: List[String] = Nil): List[String] = xs match {
case Nil => out.reverse
case x :: Nil => mergeLines(Nil, x :: out)
case x :: y :: rest =>
if (x endsWith """\""") mergeLines(x.init + y :: rest, out)
else mergeLines(y :: rest, x :: out)
}
Code to determine the lat element of a list, using pattern matching:
#tailrec
def last_rec[A](list : List[A]) : A = {
list match {
case (x :: Nil) => x
case (_ :: xs) => last_rec(xs)
case Nil => throw new NoSuchElementException
}
}
I want to compile the code, I am getting "yelled" by the compiler:
PS D:\workspace\scala\P99> scalac .\P01.scala
.\P01.scala:18: error: could not optimize #tailrec annotated method last2: it contains a recursive call not in tail position
case Nil => throw new NoSuchElementException
^
one error found
If I remove the #tailrec annotation - the code compiles . How can I modify the code in order to do the tail rec optimization ?
You got a typo their. Your method is called last_rec and you are calling last which is clearly undefined. So just rename it to last. And by the way you should return Option[A] instead of A. That way you can return None when nothing is found instead of throwing the ugly NoSuchElementException.
After removing the typo and adding agilesteel's suggestion:
#tailrec
def last_rec[A](list : List[A]) : Option[A] = {
list match {
case (x :: Nil) => Some(x)
case Nil => None
case (_ :: xs) => last_rec(xs)
}
}
In this case I would do what agilesteel suggested.
However, if you really wanted to throw an exception (in another different use case), you could do it in a statically typed way:
#tailrec
def last_rec[A](list : List[A]) : Either[NoSuchElementException,A] = {
list match {
case (x :: Nil) => Right(x)
case (_ :: xs) => last_rec(xs)
case Nil => Left(new NoSuchElementException)
}
}
where later you could:
last_rec(Nil) match {
case Right(s) => println("Got a value")
case Left(e) => println("Got an exception")
}