import scala.annotation.tailrec
object CollatzConjecture {
private object Status extends Enumeration {
type Status = Value
val ILLEGAL, EVEN, ODD, ONE = Value
}
import Status._
private def getStatus(num: Integer): Status = {
if (num <= 0)
ILLEGAL
else if (num == 1)
ONE
else if (num % 2 == 0)
EVEN
else
ODD
}
@tailrec
private def collatzMeRecur(steps: Int, num: Int): Option[Int] = {
getStatus(num) match {
case ILLEGAL => None
case ONE => Some(steps)
case EVEN => collatzMeRecur(steps + 1, num / 2)
case ODD => collatzMeRecur(steps + 1, (num * 3) + 1)
}
}
def steps(start: Int) = {
collatzMeRecur(0, start)
}
}
This approach starts by importing from packages for what will be needed.
The CollatzConjecture
class starts by defining an Enumeration in the version 2 way.
With version 3, an enum
can be created like so
enum Status:
case ILLEGAL, EVEN, ODD, ONE
The getStatus()
method returns an enum
value depending on the value of the passed-in Int
.
The collatzMeRecur()
method is annotated with the @tailrec
annotation to verify that the method can be compiled
with tail call optimization.
A tail call is a particular form of recursion where the last call in the method is a call to the same method and nothing else.
In other words, if the last call in recurMe()
is recurMe(arg1, arg2) + 1
, the + 1
makes the recursion non-tail recursive.
If the last call in recurMe()
is recurMe(arg1, arg2, acc + 1)
, then the recursion is a tail call, because only the method is being called
with no other operation being peformed on it.
A match
expression is used to perform pattern matching on the result of passing in the number to
the getStatus()
method.
If the Status
of the number is ILLEGAL
, then None
is returned.
If the Status
of the number is ONE
, then the number of steps wrapped in Some
is returned from the method.
If the Status
of the number is EVEN
or ODD
, then the function calls itself by adding 1
to steps
and calculating
the new value for the number according to whether it is even or odd.
The steps()
method calls collatzMeRecur()
, passing in 0
as the starting value for steps
and the number passed into the steps()
method as the number to be recalculated until it reaches 1
.
Shortening
All of the ceremony involving the enumeration could be gotten rid of by using pattern guards, and the code shortened to
import scala.annotation.tailrec
object CollatzConjecture {
@tailrec
private def collatzMeRecur(steps: Int, num: Int): Option[Int] = {
num match {
case nbr0 if nbr0 <= 0 => None
case 1 => Some(steps)
case num2 if num2 % 2 == 0 => collatzMeRecur(steps + 1, num2 / 2)
case num3 => collatzMeRecur(steps + 1, (num3 * 3) + 1)
}
}
def steps(start: Int) = {
collatzMeRecur(0, start)
}
}