Runtime Exception during executing a function - scala

I am trying to run a function in scala
def sum(xs: List[Int]): Int = xs match {
case Nil => throw new java.util.NoSuchElementException("Minimum number of elements")
case x :: xs => x + sum(xs)
}
When I try to run like this,
sum(List(1,2,3))
I am getting the runtime exception
java.util.NoSuchElementException: Minimum number of elements
at .sum(<console>:12)
at .sum(<console>:13)
On the other hand, this works
def sum(xs: List[Int]): Int = xs match {
case Nil => 0
case x :: xs => x + sum(xs)
}

List(1,2,3) is equivalent to 1::2::3::Nil
so your function is evaluated in following order
sum(1::2::3::Nil) = 1 + sum(2::3::Nil)
sum(2::3::Nil) = 2 + sum(3::Nil)
sum(3::Nil) = 3 + sum(Nil)
and at last sum(Nil) throws exception.
You can get more information from following question.
Why is Nil required at the end of a list built using the cons operator
Why do we need Nil while creating List in scala?

Related

Recursive function call in Scala

I am trying to write a recursive sum function as:
val sumRecursive = (list: List[Int]) => list match {
case Nil => 0
case x::xs => x + sumRecursive(xs)
}
It gives error:
Error:(16, 23) recursive value sumRecursive needs type
case x::xs => x + sumRecursive(xs)
I understand that recursive function needs to declare their return type. But I am not sure how to do it in this code structure.
As it complains for the absence of an explicit type, you can provide it the same way you would specify a classical type (val a: Int = 5):
val sumRecursive: List[Int] => Int =
list => list match {
case Nil => 0
case x::xs => x + sumRecursive(xs)
}
which gives:
scala> sumRecursive(List(1, 2, 3))
res0: Int = 6
To perform the analogy with val a: Int = 5,
a is sumRecursive
Int is List[Int] => Int
5 is list => list match { case Nil => 0; case x::xs => x + sumRecursive(xs) }
Maybe a tail recursive function will be better if your list is too long.
val sumRecursive: (List[Int], Int) => Int =
(list, acc) => list match {
case Nil => acc
case x :: xs => sumRecursive(xs, x + acc)
}
Try this
call it like this:
sumRecursive(List(1, 2, 3, 4, 5), 0)
0 is the accumulator that will be incremented to hold the sum value
As it is asking for the type annotation, in Scala the recursive function are not able to infer the return type of the function so. That’s something we need to do in your case it will be the Int.
Just type annotate the method like this.
val sumRecursive :Int= (list: List[Int]) => list match {
case Nil => 0
case x::xs => x + sumRecursive(xs)}
How Int I think you want to know that.
Suppose you have 3 elements in the list: 1,2,3
sunRecursive(list): Int
It will go into the case x::xs which means
x is the head of the list and xs is the tail.
1st step
So you do 1 + sumRecursive(xs) //xs=2, 3
2nd step 2+sumResursive(xs) //xs=3
3rd step 3 +sumResursive(xs) //xs=Nil
It will go into the first case and return 0.
So 3rd step willreturn 3+0 to the second step
It will become2+3 and return to the 1st step
It will become1+2+3 which is 6 and it will return 6
So the return type will be Int ultimately.

Compress a Given Text of String in Scala

I have been trying to compress a String. Given a String like this:
AAABBCAADEEFF, I would need to compress it like 3A2B1C2A1D2E2F
I was able to come up with a tail recursive implementation:
#scala.annotation.tailrec
def compress(str: List[Char], current: Seq[Char], acc: Map[Int, String]): String = str match {
case Nil =>
if (current.nonEmpty)
s"${acc.values.mkString("")}${current.length}${current.head}"
else
s"${acc.values.mkString("")}"
case List(x) if current.contains(x) =>
val newMap = acc ++ Map(acc.keys.toList.last + 1 -> s"${current.length + 1}${current.head}")
compress(List.empty[Char], Seq.empty[Char], newMap)
case x :: xs if current.isEmpty =>
compress(xs, Seq(x), acc)
case x :: xs if !current.contains(x) =>
if (acc.nonEmpty) {
val newMap = acc ++ Map(acc.keys.toList.last + 1 -> s"${current.length}${current.head}")
compress(xs, Seq(x), newMap)
} else {
compress(xs, Seq(x), acc ++ Map(1 -> s"${current.length}${current.head}"))
}
case x :: xs =>
compress(xs, current :+ x, acc)
}
// Produces 2F3A2B1C2A instead of 3A2B1C2A1D2E2F
compress("AAABBCAADEEFF".toList, Seq.empty[Char], Map.empty[Int, String])
It fails however for the given case! Not sure what edge scenario I'm missing! Any help?
So what I'm actually doing is, going over the sequence of characters, collecting identical ones into a new Sequence and as long as the new character in the original String input (the first param in the compress method) is found in the current (the second parameter in the compress method), I keep collecting it.
As soon as it is not the case, I empty the current sequence, count and push the collected elements into the Map! It fails for some edge cases that I'm not able to make out!
I came up with this solution:
def compress(word: List[Char]): List[(Char, Int)] =
word.map((_, 1)).foldRight(Nil: List[(Char, Int)])((e, acc) =>
acc match {
case Nil => List(e)
case ((c, i)::rest) => if (c == e._1) (c, i + 1)::rest else e::acc
})
Basically, it's a map followed by a right fold.
Took inspiration from the #nicodp code
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((lastChar, lastCharCount) :: xs) if lastChar == e => (lastChar, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
First our intermediate result will be List[(Char, Int)]. List of tuples of chars each char will be accompanied by its count.
Now lets start going through the list one char at once using the Great! foldLeft
We will accumulate the result in the acc variable and e represents the current element.
acc is of type List[(Char, Int)] and e is of type Char
Now when we start, we are at first char of the list. Right now the acc is empty list. So, we attach first tuple to the front of the list acc
with count one.
when acc is Nil do (e, 1) :: Nil or (e, 1) :: acc note: acc is Nil
Now front of the list is the node we are interested in.
Lets go to the second element. Now acc has one element which is the first element with count one.
Now, we compare the current element with the front element of the list
if it matches, increment the count and put the (element, incrementedCount) in the front of the list in place of old tuple.
if current element does not match the last element, that means we have
new element. So, we attach new element with count 1 to the front of the list and so on.
then to convert the List[(Char, Int)] to required string representation.
Note: We are using front element of the list which is accessible in O(1) (constant time complexity) has buffer and increasing the count in case same element is found.
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((lastChar, lastCharCount) :: xs) if lastChar == e => (lastChar, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
// Exiting paste mode, now interpreting.
encode: (word: String)String
scala> encode("AAABBCAADEEFF")
res0: String = 3A2B1C2A1D2E2F
Bit more concise with back ticks e instead of guard in pattern matching
def encode(word: String): String =
word.foldLeft(List.empty[(Char, Int)]) { (acc, e) =>
acc match {
case Nil => (e, 1) :: Nil
case ((`e`, lastCharCount) :: xs) => (e, lastCharCount + 1) :: xs
case xs => (e, 1) :: xs
}
}.reverse.map { case (a, num) => s"$num$a" }.foldLeft("")(_ ++ _)
Here's another more simplified approach based upon this answer:
class StringCompressinator {
def compress(raw: String): String = {
val split: Array[String] = raw.split("(?<=(.))(?!\\1)", 0) // creates array of the repeated chars as strings
val converted = split.map(group => {
val char = group.charAt(0) // take first char of group string
s"${group.length}${char}" // use the length as counter and prefix the return string "AAA" becomes "3A"
})
converted.mkString("") // converted is again array, join turn it into a string
}
}
import org.scalatest.FunSuite
class StringCompressinatorTest extends FunSuite {
test("testCompress") {
val compress = (new StringCompressinator).compress(_)
val input = "AAABBCAADEEFF"
assert(compress(input) == "3A2B1C2A1D2E2F")
}
}
Similar idea with slight difference :
Case class for pattern matching the head so we don't need to use if and it also helps on printing end result by overriding toString
Using capital letter for variable name when pattern matching (either that or back ticks, I don't know which I like less :P)
case class Count(c : Char, cnt : Int){
override def toString = s"$cnt$c"
}
def compressor( counts : List[Count], C : Char ) = counts match {
case Count(C, cnt) :: tail => Count(C, cnt + 1) :: tail
case _ => Count(C, 1) :: counts
}
"AAABBCAADEEFF".foldLeft(List[Count]())(compressor).reverse.mkString
//"3A2B1C2A1D2E2F"

Type mismatch when using iterators

I'm getting this error when i run the below code -
type mismatch, found : scala.collection.immutable.IndexedSeq[Int] required: Range
Where I'm going wrong ?
Functions -
def calcRange(i: Int, r: List[Range]):List[Range] = r match {
case List() => List(new Range(i,i+1,1))
case r1::rs =>
if (r1.start-1==i) {new Range(i,r1.end,1):: rs; }
else if(r1.end==i){new Range(r1.start,r1.end+1,1)::rs}
else {r1::calcRange(i,rs)}
}
def recurseForRanges(l: Iterator[Int]):List[Range] = {
var ans=List[Range]()
while(l.hasNext){
val cur=l.next;
ans=calcRange(cur,ans)
}
ans
}
def rangify(l: Iterator[Int]):Iterator[Range] = recurseForRanges(l).toIterator
Driver code
def main(args: Array[String]) {
val x=rangify( List(1,2,3,6,7,8).toIterator ).reduce( (x,y) => x ++ y)
/** This line gives the error -type mismatch,
found : scala.collection.immutable.IndexedSeq[Int] required: Range */
}
You can check docs:
++[B](that: GenTraversableOnce[B]): IndexedSeq[B]
++ returns IndexedSeq, not another Range, Range cannot have "holes" in them.
One way to fix it is to change Ranges to IndexedSeqs before reducing. This upcasts the Range so that reduce could take function
(IndexedSeq[Int], IndexedSeq[Int]) => IndexedSeq[Int]
because now it takes
(Range, Range) => Range
But ++ actually returns IndexedSeq[Int] instead of Range hence the type error.
val x = rangify(List(1, 2, 3, 6, 7, 8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
You can as well do this kind of cast by annotating type:
val it: Iterator[IndexedSeq[Int]] = rangify(List(1,2,3,6,7,8).iterator)
val x = it.reduce(_ ++ _)
Note that your code can be simplified, without vars
def calcRange(r: List[Range], i: Int): List[Range] = r match {
case Nil =>
Range(i, i + 1) :: Nil
case r1 :: rs =>
if (r1.start - 1 == i)
Range(i, r1.end) :: rs
else if (r1.end == i)
Range(r1.start, r1.end + 1) :: rs
else
r1 :: calcRange(rs, i)
}
def recurseForRanges(l: Iterator[Int]): List[Range] = {
l.foldLeft(List.empty[Range])(calcRange)
}
def rangify(l: Iterator[Int]): Iterator[Range] = recurseForRanges(l).iterator
val x = rangify(List(1,2,3,6,7,8).iterator).map(_.toIndexedSeq).reduce(_ ++ _)
To explain what I've done with it:
Range has a factory method, you don't need new keyword, you don't need to specify by value because 1 is default.
You need no semicolons as well.
What you are doing in recurseForRanges is basically what foldLeft does, I just swapped arguments in calcRange it could be passed directly to foldLeft.

Scala List match last element

I'm currently learning Scala and I'm amazed. The language handles so much problems quite elegant. But, I got a problem when it comes to matching the last element of a list.
Let's have a look at this code:
def stringify(list: List[String]): String = list match {
case x :: xs => x + (if (xs.length != 0) ":" else "") + stringify(xs)
case Nil => ""
}
This is quite inelegant and I would like to write it more intuitive, something like this:
def stringify(list: List[String]): String = list match {
case x :: xs => x + ":" + stringify(xs)
case x :: Nil => x
}
How can I do that?
You need to switch the order. The xs symbol will eagerly match anything in that position. Trying to match Nil first will make that statement no longer unreachable. Also, you'll still need to match Nil by itself to account for empty lists.
def stringify(list: List[String]): String = list match {
case x :: Nil => x
case x :: xs => x + ":" + stringify(xs)
case Nil => ""
}
Though mkString already does what you're looking to make.
Here's an implementation using List#foldRight:
def stringify(list: List[String]): String =
list.foldRight(""){
(e, acc) => if (acc.isEmpty) e
else { e + ":" + acc }
}
When folding over this list, we need to check for when the accumulator is empty. Otherwise, we'll get an extra : at the end of the stringfied string result.
Tests
scala> stringify(List("1", "2" , "3") )
res6: String = 1:2:3
scala> stringify(List("1", "2" , "3", "500") )
res7: String = 1:2:3:500

How to properly use fold left in a scala function

I've a problem with this part of code in scala
object Test12 {
def times(chars: List[Char]): List[(Char, Int)] = {
val sortedChars = chars.sorted
sortedChars.foldLeft (List[(Char, Int)]()) ((l, e) =>
if(l.head._1 == e){
(e, l.head._2 + 1) :: l.tail
} else {
(e, 1) :: l
} )
}
val s = List('a', 'b')
val c = times s
}
The last line give an error :
Missing arguments for method times; follow this method with `_' if you
want to treat it as a partially applied function
But I don't see why, because I've given 2 arguments to the last function - foldLeft.
Thanks in advance for help!
The idea of code is to count how much time each character is present in a given list
The syntax of times is fine, but you need to use parenthesis when calling it, i.e. :
val c = times(s)
But it won't work because you use l.head without checking if l is Nil, and an empty list does not have a head. You can e.g. check with match for that:
def times(chars: List[Char]): List[(Char, Int)] = {
val sortedChars = chars.sorted
sortedChars.foldLeft (List[(Char, Int)]()) ((a,b) => (a,b) match {
case (Nil, e) => (e, 1) :: Nil
case ((e, count) :: l, f) =>
if (e == f) (e, count + 1) :: l
else (f, 1) :: (e, count) :: l
})
}
Although an easier way is to use the higher level collection functions:
def times(chars: List[Char]) = chars.groupBy(c=>c).map(x=>(x._1,x._2.length)).toList
val c = times s
You can't call method without brackets like this. Try times(s) or this times s.