I have a scala code.
val lines = Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1) match {
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
when I run the code it is working how ever there is a warning message
Warning:(81, 14) match may not be exhaustive.
It would fail on the following input: Nil
.drop(1) match {
Please suggest how to remove this warning.
Just add a case of Nil:
val lines = Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1) match {
case Nil => List.empty // Add this line
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
you can just add the Nil case to your pattern matching.
if you really wanna suppress the warning you could use the unchecked annotation:
val lines = (Source
.fromResource("doc-topics-new.txt")
.getLines
.toList
.drop(1): #unchecked) match {
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
Related
Been trying to solve this for a while now. I need a recursive function that removes all None's from a list of Option[Int]'s, without using if-statements or using other functions.
def RemoveNone2(input: List[Option[Int]]) : List[Int] = {
input.head match {
case None => RemoveNone2(input.tail)
case _ => (input.head.get::RemoveNone2(input.tail))
}
}
val optional: List[Option[Int]] = List(Some(13), None, Some(32), None, Some(51), None, Some(17), None)
RemoveNone2(optional)
But when trying to execute the code, I get the following error:
java.util.NoSuchElementException: head of empty list
I suck at Scala, could anyone offer some insight?
You want headOption:
def RemoveNone2(input: List[Option[Int]]) : List[Int] = input.headOption match {
case None => RemoveNone2(input.tail)
case Some(head) => head :: RemoveNone2(input.tail)
}
A better way to write this is:
def removeNone(input: List[Option[Int]]) : List[Int] = input match {
case Nil => Nil
case Some(head) :: tail => head :: removeNone(tail)
case None :: tail => removeNone(tail)
An even better way is to use an accumulator, so that you can take advantage of tail-recursion:
def removeNone(input: List[Option[Int]], out: List[Int]=Nil) : List[Int] = input match {
case Nil => out.reverse
case Some(head) :: tail => removeNone(tail, head :: out)
case None :: tail => removeNone(tail, out)
}
You need to check that input list is empty to break the recursion. One of the options is to match against the list itself:
def RemoveNone2(input: List[Option[Int]]) : List[Int] = input match {
case head :: tail => head match {
case Some(value) => value :: RemoveNone2(tail)
case _ => RemoveNone2(tail)
}
case _ => Nil
}
Also note, that this implementation is not tail-recursive, whcih can lead to errors or poor performance for big collections. Tail-recursive implementation can look like that:
def RemoveNone2(input: List[Option[Int]]) : List[Int] = {
#annotation.tailrec
def inner(innerInput: List[Option[Int]], acc: List[Int]): List[Int] = innerInput match {
case head :: tail => head match {
case Some(value) => inner(tail, value::acc)
case _ => inner(tail, acc)
}
case _ => acc
}
inner(input, Nil)
}
I need to write a function to analyze some text files.
For that, there should be a function that splits the file via a predicate into sublists. It should only get the values after the first time the predicate evaluates to True and afterwards start a new sublist after the predicate was True again.
For Example:
List('ignore','these','words','x','this','is','first','x','this','is','second')
with predicate
x=>x.equals('x')
should produce
List(List('this','is','first'),List('this','is','second'))
I've already done the reading of the file into a List[String] and tried to use foldLeft with a case statement to iterate over the List.
words.foldLeft(List[List[String]]()) {
case (Nil, s) => List(List(s))
case (result, "x") => result :+ List()
case (result, s) => result.dropRight(1) :+ (result.last :+ s)
}
There are 2 problems with this though and I can't figure them out:
This does not ignore the words before the first time the predicate
evaluates to True
I can't use an arbitrary predicate function
If anyone could tell me what I have to do to fix my problems it would be highly appreciated.
I modified your example a little bit:
def foldWithPredicate[A](predicate: A => Boolean)(l: List[A]) =
l.foldLeft[List[List[A]]](Nil){
case (acc, e) if predicate(e) => acc :+ Nil //if predicate passed add new list at the end
case (Nil, _) => Nil //empty list means we need to ignore elements
case (xs :+ x, e) => xs :+ (x :+ e) //append an element to the last list
}
val l = List("ignore","these","words","x","this","is","first","x","this","is","second")
val predicate: String => Boolean = _.equals("x")
foldWithPredicate(predicate)(l) // List(List(this, is, first), List(this, is, second))
There's one problem performance related to your approach: appending is very slow on immutable lists.
It might be faster to prepend elements on the list, but then, of course, all lists will have elements in reversed order (but they could be reversed at the end).
def foldWithPredicate2[A](predicate: A => Boolean)(l: List[A]) =
l.foldLeft[List[List[A]]](Nil){
case (acc, e) if predicate(e) => Nil :: acc
case (Nil, _) => Nil
case (x :: xs, e) => (e :: x) :: xs
}.map(_.reverse).reverse
An alternative approach is to use span to split the items into the next sublist and the rest in a single call. The following code assumes Scala 2.13 for List.unfold:
def splitIntoBlocks[T](items: List[T])(startsNewBlock: T => Boolean): List[List[T]] = {
def splitBlock(items: List[T]): (List[T], List[T]) = items.span(!startsNewBlock(_))
List.unfold(splitBlock(items)._2) {
case blockIndicator :: rest => Some(splitBlock(rest))
case _ => None
}
}
And the usage:
scala> splitIntoBlocks(List(
"ignore", "these", "words",
"x", "this", "is", "first",
"x", "this", "is", "second")
)(_ == "x")
res0: List[List[String]] = List(List(this, is, first), List(this, is, second))
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"
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)
}
What is the best way (concisest, clearest, idiomatic) to catch a MatchError, when assigning values with pattern matching?
Example:
val a :: b :: Nil = List(1,2,3) // throws scala.MatchError
The best way I found so far:
val a :: b :: Nil = try {
val a1 :: b1 :: Nil = List(1,2,3)
List(a1, b1)
catch { case e:MatchError => // handle error here }
Is there an idiomatic way to do this?
Why not simply
val a::b::Nil = List(1,2,3) match {
case a1::b1::Nil => {
a1::b1::Nil
}
case _ => //handle error
}
?
Slightly improving on Kim's solution:
val a :: b :: Nil = List(1, 2, 3) match {
case x # _ :: _ :: Nil => x
case _ => //handle error
}
If you could provide more information on how you might handle the error, we could provide you a better solution.
The following doesn't catch the error but avoids (some of; see Nicolas' comment) it. I don't know whether this is interesting to the asker.
scala> val a :: b :: _ = List(1,2,3)
a: Int = 1
b: Int = 2
The simple solution is this:
List(1, 2, 3) match {
case a :: b :: Nil => .....
case _ => // handle error
}
I don't like to match twice, because it is redundant.
The "val" with pattern matching should only be used when you are sure it matches, or add a try /catch block.