Recursion

Phone Number
Phone Number in Scala
object PhoneNumber {

  def clean(input: String): Option[String] = {
    cleanRecur(input.to(List), Nil)
  }

  @scala.annotation.tailrec
  private def cleanRecur(
      raw: List[Char],
      cleaned: List[Char]
  ): Option[String] = {
    (raw, cleaned.length) match {
      case (Nil, index) => {
        if (index == 10) return Some(cleaned.reverse.mkString) else None
      }
      case (head :: tail, index) if List(0, 3).contains(index) => {
        head.isDigit match {
          case true if (head > '1') => cleanRecur(tail, head :: cleaned)
          case true if (index == 0 && head == '1') => cleanRecur(tail, cleaned)
          case false if (index == 0 && List('+', '(', ' ').contains(head)) =>
            cleanRecur(tail, cleaned)
          case false if (index == 3 && List(')', ' ', '.').contains(head)) =>
            cleanRecur(tail, cleaned)
          case _ => None
        }
      }
      case (head :: tail, index) => {
        head.isDigit match {
          case true => cleanRecur(tail, head :: cleaned)
          case false if (index == 6 && List(' ', '-', '.').contains(head)) =>
            cleanRecur(tail, cleaned)
          case false if (head == ' ') => cleanRecur(tail, cleaned)
          case _                      => None
        }
      }
    }
  }
}

The clean() method returns the result of calling the recursive method, passing in the input String converted to a List and an empty List for building the output String.

The recursive 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.

In the recursive method, the input List and the length of the output List are matched. The length of the output List is used for the index value.

If the input List is Nil (meaning it is empty), then the index is checked. If the index is 10, then the reverse and mkString methods are used on the output List to return a Some value from the recursive method. If the index is not 10, then None is returned from the recursive method.

Otherwise, the cons operator (::) is used to destructure the head and tail from the input List, and the index is checked for being 1 or 3. If so, and the head is a digit greater than 1, then the method calls itself, passing in the tail of the input List and the head prepended to the output List with the cons operator (::). If the head is some other legal character for its index, then the method calls itself with the tail of the input List and the output Lists as is. If the head is not a legal character for its index, then the method returns None.

If the index is neither 0 or 3, then the cons operator (::) is used to destructure the head and tail from the input List, and the head is checked for being a digit. If it is a digit, then the method calls itself, passing in the tail of the input List and the head prepended to the output List with the cons operator (::). If the head is not a digit but is some other legal character, then the method calls itself with the tail of the input List and the output Lists as is. If the head is not a legal character, then the method returns None.

11th Sep 2024 · Found it useful?