Would you consider the following block of code match abuse and if so what's a more elegant way to do it without a big if-else-if block?
def sum(base: Int, xs: List[Int]): Int = {
base match {
case 0 => 1
case _ if (base < 0) => 0
case _ if (xs.isEmpty) => 0
case _ => xs.sum
}
}
Yes, this an abuse of match. You've basically just written a big if-else-if block, but in a more awkward form. What's wrong with if-statements?
I think it's much cleaner to just write this:
def countChange(money: Int, coins: List[Int]): Int = {
if(money == 0) 1
else if (money < 0) 0
else if (coins.isEmpty) 0
else countChange(money, coins.tail) + countChange(money - coins.head, coins)
}
If you want to stick with a match, you can move more of the checking into the match itself, so that it's actually doing something:
def countChange(money: Int, coins: List[Int]): Int = {
(money, coins) match {
case (0, _) => 1
case _ if (money < 0) => 0
case (_, Nil) => 0
case (_, coinsHead :: coinsTail) => countChange(money, coinsTail) + countChange(money - coinsHead, coins)
}
}
No. Why abuse? It's fairly readable IMO...
The problem I see is that money match ... is fairly arbitrary (you only use the direct pattern in the first case); a complete "abuse" would start like
() match {
case _ if (money == 0) => 1
...
So perhaps stick to if-else; you can combine the second and third condition (if( money < 0 || coins.isEmpty ) ...)
Also note that although you "know" in the end that coins is not empty and thus may "safely" call head and tail on it, this is a typical source of unexpected runtime errors. The advantage of coins match { case Nil => ...; case head :: tail => ...} is that you cannot make such a mistake.
Related
I would like to do pattern matching on list items using if statements. However, I get an error at the space before my if statement saying "Illegal start of simple pattern". What would be the correct way to implement pattern matching on list items with IF statements?
def myMethod(xs: List[Int]): Int= {
xs match {
case Nil => 0
case i :: if (i % 2 == 0) => i
}
}
You are missing _ before the guard if
def myMethod(xs: List[Int]): Int = xs match {
case Nil => 0
case i :: _ if (i % 2 == 0) => i
}
I'm writing a function and having a strange issue. I'm using pattern matching, and then an internal function which uses a slightly changed but almost identical pattern and it isn't compiling:
def isTriangular(n: Int): Boolean = {
n match {
case n if n < 1 => false
case _ => triangularMaths(n, 1)
}
def triangularMaths(j:Int, counter: Int): Boolean = (j, counter) match {
case _ if j-counter == 0 => true
case _ if j-counter < 0 => false
case _ => triangularMaths(j-counter, counter+1)
}
}
The fix for this is I simply make them two seperate methods, and they work as long as triangularMaths isn't nested. However, since triangularMaths is specific to triangular only, I'd like it to be nested. However, when I do this, my compiler complains, telling me I am returning Unit, rather than the expected Boolean . This doesn't quite make sense, as once the original case brackets is resolved, returning true or false, it should go to the end of the method, and complete, correct? What's the fix?
This happens because your method is the last declaration in scope, which makes the compiler emit the Unit value as the return type. Decompiled code looks like this:
def main(args: Array[String]): Unit = {
def isTriangular(n: Int): Boolean = {
n match {
case (n # _) if n.<(1) => false
case _ => triangularMaths(n, 1)
};
def triangularMaths(j: Int, counter: Int): Boolean = scala.Tuple2.apply[Int, Int](j, counter) match {
case _ if j.-(counter).==(0) => true
case _ if j.-(counter).<(0) => false
case _ => triangularMaths(j.-(counter), counter.+(1))
};
()
};
First define triangularMaths, and then invoke it:
def isTriangular(n: Int): Boolean = {
def triangularMaths(j: Int, counter: Int): Boolean = (j, counter) match {
case _ if j - counter == 0 => true
case _ if j - counter < 0 => false
case _ => triangularMaths(j - counter, counter + 1)
}
n match {
case n if n < 1 => false
case _ => triangularMaths(n, 1)
}
}
Another possibility would be to assign the pattern match to a value, and then return that value as the last expression of the method. But, that would make the compiler complain about forward reference, which you could fix by making it a lazy val instead. I would stick the the re-ordering approach.
I am trying to write a recursive function in SCALA that take sin a list and sorts it.
However, the code seems to run for a long time. It doesn't even give me an error message.
def sort(list:List[Int]):List[Int] = list match{
case Nil => Nil
case h::t => insert (h, sort(t))
def insert(num:Int, list:List[Int]): List[Int]=list match{
case Nil => List()
case head::tail=>
if(head>=num)
num::list
else
head::insert(num,tail)
}
sort(list)
}
You had 2 problems:
1) you are calling sort recursively from sort function directly - remove sort(list) because insert already calls it. This will make it terminate.
2) you return an empty list in one of the cases instead of constructing a list with 1 elem - base case is wrong.
This version works:
def sort(list: List[Int]): List[Int] = {
def insert(num: Int, list: List[Int]): List[Int] = list match {
case Nil => num :: Nil
case head::tail =>
if(head >= num)
num::list
else
head::insert(num, tail)
}
list match {
case Nil => Nil
case h::t => insert(h, sort(t))
}
}
I know that parametric polymorphism is what actually works, but I'm curious why using Any in it's place does not. For example how is the first function
def len[T] (l:List[T]):Int =
l match {
case Nil => 0
case _ :: t => 1 + len(t)
}
different from this one?
def len (l:List[Any]):Int =
l match {
case Nil => 0
case _ :: t => 1 + len(t)
}
What do you mean it doesn't work? This seems fine:
len(List('a,'b,'c))
// res0: Int = 3
Your in your example, there really isn't a difference, since you're not actually using the contents of the list for anything, but imagine a slightly different function:
def second[T](l: List[T]): Option[T] =
l match {
case Nil => None
case _ :: Nil => None
case _ :: x :: _ => Some(x)
}
println(second(List(1,2,3)).map(_ + 5)) // Some(7)
println(second(List(List('a,'b,'c), List('d,'e))).map(_.head)) // Some('d)
If you tried this with Any, you wouldn't be able to get anything except Option[Any] in return, so the compiler wouldn't let you do anything useful with the result (like add it to an Int or call .head, as in the examples, respectively).
In this case there really isn't a difference, because you aren't relying on the contained type at all, just the structure of List itself. It doesn't matter what T is, the length will be the same either way.
The type parameter would be important if you wanted to return another List[T]. For example:
def takeEveryOther[T](l: List[T]): List[T] =
l.zipWithIndex.collect { case (a, i) if(i % 2 == 0) => a }
I have a method that is recursive. Is there a way in scala to break out based on the size of the buffer (as shown below)? A case for breaking out when elementList.size > 5 for example?
val elementList = ListBuffer.empty[Book]
#tailrec
def getBooks(elements: List[Element]) {
elements match {
case Nil => info("Reached end of elements list.")
case element :: rest if element.getElementType == ElementType.BOOK => {
elementList.append(element.getBook)
getLooks(rest)
}
case _ => getBooks(elements.tail)
}
}
I guess the most simple way would be to just wrap an if statement around your match statement like this:
val elementList = ListBuffer.empty[Book]
#tailrec
def getBooks(elements: List[Element]) {
if (elementList.size <= 5){
elements match {
case Nil => info("Reached end of elements list.")
case element :: rest if element.getElementType == ElementType.BOOK => {
elementList.append(element.getBook)
getLooks(rest)
}
case _ => getBooks(elements.tail)
}
}
}
Genereally speaking you could try to pass down the number of remaining elements in the recursion.
For example:
def get(list: List[Int], max: Int): List[Int] = {
#tailrec
def get(list: List[Int], acc: List[Int], remaining: Int): List[Int] = {
list match {
case h :: tail if remaining > 0 =>
get(tail, h :: acc, remaining - 1)
case _ =>
acc
}
}
// Requires reverse() !
get(list, Nil, max).reverse
As for the accumulator: you could use a buffer instead, to prevent the reverse() at the end.