filter and map with Stream and takeWhile

Collatz Conjecture
Collatz Conjecture in Scala
object CollatzConjecture {

  def steps(start: Int): Option[Int] = {
    Option(start)
      .filter(_ > 0)
      .map(collatzStream(_).takeWhile(_ != 1).length)
  }
  
  private def collatzStream(n: Int): Stream[Int] =
    Stream.iterate(n)(n => if (n % 2 == 0) n / 2 else (n * 3) + 1)
}

This approach starts by creating an Option for the passed in Int which creates a Some value for it if it is not null.

The Option is chained to filter(), which transforms a Some value to None if it does not satisfy its lambda function. Here, the lambda checks that the value is greater than 0. Since the name of the value is not needed, the underscore _ is used to avoid naming the variable passed into the lambda. So, .filter(_ > 0) is used instead of something like .filter(num => num > 0).

The Option from filter() is chained to the map() method. If the Option is not None, then map() calls the collatzStream() method, passing in the Option's value. It it is passed as _ since its name isn't needed to make the call.

The collatzStream() method starts by calling the Stream.iterate() method which repeatedly calls its lambda. iterate() is passed a starting value of the number passed in to the collatzStream() method. That number is continually modified by the result of the lambda. It does that by using a ternary expression that calculates the new value depending on whether the number is evenly divisible by 2 or not. It would go on forever except that the Stream is chained to the takeWhile() method whose lambda keeps asking for an iteration of the Stream as long as the number being calculated does not equal 1. Once the takeWhile() condition returns false, the length of the numbers generated in the Stream sequence is returned from the map() method which is then returned from the steps() method.

9th Oct 2024 · Found it useful?