Let's say I have a Tree object with 2 member objects, right and left.
What's the idiomatic/right way to check if tree's "right" and "left" fields are Nil?
def count(tree: Tree, acc: Int) : Int = tree match {
case tree .right != Nil && tree .left != Nil => countLeftAndRight(...)
case tree .right != Nil => countOnlyRight(...)
case tree .left != Nil => countOnlyLeft(...)
_ => acc
}
Your example isn't valid Scala, but the idiomatic way to match the Tree is with an extractor (look it up). You get this for free if Tree is a case class. Assuming it is, you can write
tree match {
case Tree(Nil, Nil) => acc
case Tree(Nil, x) => ...
case Tree(x, Nil) => ...
case Tree(x, y) => ...
}
Or, if you'd prefer to work with your non-case-class Tree as-is, then you might try this variation of Luigi's solution:
(tree.left, tree.right) match {
case (Nil, Nil) => acc
case (Nil, x) => ...
case (x, Nil) => ...
case (x, y) => ...
Related
I'm having a def that is recursively called and I'm using a bunch of cases.
I'm wondering is there is any nice solution to get rid of that cases, without losing the readability of the definition.
#tailrec
def getElements(existent:List[List[String]], proposed: List[List[String]], result: List[(String,List[String])]): List[(String, List[String])]= {
proposed match {
case head :: tail => {
existent.find(element => identicalOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Same", elem) :: result)
case None => {
existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), head::tail, ("Delete", elem) :: result)
case None => {
existent.find(element => firstOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)
case None => {
existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail, ("Update", head) :: result)
case None => getElements(Nil, Nil, existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: result)
}
}
}
}
}
}
}
}
case Nil => result
}
}
Your method readability would benefit greatly if you could split it into several methods.
Unfortunately, you can't do that if you want your function to be tail-recursive.
But there is a solution which is called trampoline, which would allow you to create an arbitrarily deep chain of function recursive calls which are stack-safe.
Trampolines are implemented in Scala standard library in package scala.util.control.TailCalls. I reimplemented your method to take advantage of it.
First thing I did was removing an accumulator parameter from getElements, we won't need it anymore.
Then I split getElements into three functions. I called nested functions ifNoneMatched and ifNoneMatched2 but you could probably come up with more meaningful names.
Then I wrapped every call to a function which is in the chain into tailcall and every constant into done (in our case Nil). When I needed to append something to list returned from a recursive call I used flatMap from TailRec.
import scala.util.control.TailCalls._
def getElements(existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]]= {
proposed match {
case head :: tail => {
existent.find(element => identicalOfMatch(element, head)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), tail).map(("Same", elem) :: _))
case None => tailcall(ifNoneMatched(head, tail, existent, proposed))
}
}
case Nil => done(Nil)
}
}
def ifNoneMatched(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
existent.find(element => noneOfMatch(element, head) && canDelete(proposed, element)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), proposed)).map(("Delete", elem) :: _)
case None => tailcall(ifNoneMatched2(head, tail, existent, proposed))
}
}
def ifNoneMatched2(head: List[String], tail: List[List[String]], existent:List[List[String]], proposed: List[List[String]]): TailRec[List[(String, List[String])]] = {
existent.find(element => firstOfMatch(element, head)) match {
case Some(elem) => getElements(existent.filterNot(e => e == elem), tail).map(("Update", head) :: _)
case None => {
existent.find(element => anyOfMatch(element, head) && firstOfNotPresent(proposed, head.head) && firstOfNotPresent(existent, head.head)) match {
case Some(elem) => tailcall(getElements(existent.filterNot(e => e == elem), tail)).map(("Update", head) :: _)
case None => getElements(Nil, Nil).map(existent.map(element => ("Deleted", element)) ::: proposed.map(element => ("Created", element)) ::: _)
}
}
}
}
getElements returns now TailRec[List[(String, List[String])]], but you can unwrap it by just calling result.
Of course, you can nest methods even deeper. Until you wrap your method calls into tailcall your stack is safe.
I didn't reimplement your methods like identicalOfMatch etc. so I couldn't really test if my implementation works. If something breaks please let me know ;)
I have a the following Java snippet (where a and b are futures):
if (a.isEmpty && b.isEmpty) func(list)
else if (a.isEmpty) func(list, b)
else if (b.isEmpty) func(a, list)
else func(a, list, b)
I have all the implementations of the function 'func'.
Is there a proper way to write this in Scala or is this good enough?
Assuming a and b are lists, which seems likely as they seem related to list:
(a, b) match {
case (Nil, Nil) => func(list)
case (Nil, _) => func(list, b)
case (_, Nil) => func(a, list)
case _ => func(a, b, list)
}
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 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))
}
}
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")
}