match with recursion

Collatz Conjecture
Collatz Conjecture in Scala
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)
  }
}
11th Sep 2024 · Found it useful?