Isn’t there a way to do the following with some sort of map or flatMap or filter without the tmp variable x?
x is an Option, if it is not empty, then I’ve found what I’m looking for and I can return it, otherwise I need to make a recursive call.
val x = growByOne(c :: cap, findCapExtensions(c, cap, deck - c))
if (x.nonEmpty)
x
else
recur(cs)
The expression growByOne(c :: cap, findCapExtensions(c, cap, deck - c)).getOrElse(recur(cs)) does not work because getOrElse returns the content of the Option not the Option itself.
I need something like .ifEmpty(recur(cs))
The following is fewer lines of code, but I’m not sure whether it is clearer.
growByOne(c :: cap, findCapExtensions(c, cap, deck - c)) match {
case None => recur(cs)
case x@Some(_) => x
}
Yikes!
The following are NOT the same as they would appear. In fact the 1st one FAILS to be tail recursive.
@tailrec
def recur(remaining: List[Card]): Option[Set[Card]] = {
remaining match {
case Nil => None
case c :: cs =>
growByOne(c :: cap, findCapExtensions(c, cap, deck - c))
.orElse(recur(cs))
}
}
vs
@tailrec
def recur(remaining: List[Card]): Option[Set[Card]] = {
remaining match {
case Nil => None
case c :: cs =>
match growByOne(c :: cap, findCapExtensions(c, cap, deck - c)) {
case None => recur(cs)
case x@Some(_) => x
}
}
}
I take it that you mean the first is no tail recursive, as the call recur(cs) is not in tail postion (as .orElse is applied to the result). As far as I can see, one needs an accumulator to make that tail recursive. My attempt is below, but I cannot test it due to missing definitions.
@annotation.tailrec
def recur(remaining: List[Card], ansOpt: Option[Set[Card]] = None): Option[Set[Card]] =
ansOpt.orElse(
remaining match {
case Nil => None
case c :: cs =>
recur(cs, growByOne(c :: cap, findCapExtensions(c, cap, deck - c)))
}
)
The compiler says this is not tail recursive. in fact the call in tail position is a call to .orElse, not a call to recur. If the JVM supported real tail call optimization, then this call to .orElse in tail position could indeed be TC optimized.
Right – this is pretty much the most common and easy-to-make mistake when writing tail recursion (heaven knows I do it all the time myself). As a rule of thumb, the recursive operation can’t be a parameter to another function, or it will foil the tail recursion.