In Scala, Is it possible to have a "partially lazy" calculaltion of a dynamically calculated partial function?

95 Views Asked by At

With assistance from @Luis Miguel Mejía Suárez, I have a dynamically calculated partial function for changing arabic numbers to roman numerals:

import scala.collection.immutable.ListMap
import scala.collection.mutable.StringBuilder  

object RomanNumerals {
  private val romanNumeralByValue: ListMap[Int, String] = ListMap(
    1000 → "M",
    900 → "CM",
    500 → "D",
    400 → "CD",
    100 → "C",
    90 → "XC",
    50 → "L",
    40 → "XL",
    10 → "X",
    9 → "IX",
    5 → "V",
    4 → "IV",
    1 → "I"
  )

  private val tryRomanStep: (Int, String) => PartialFunction[Int, (Int, String)] =
    (upperLimit, result) => {
      case value if (value >= upperLimit) =>
        upperLimit -> result
    }

  private val tryRoman: PartialFunction[Int, (Int, String)] =
    romanNumeralByValue.map(tryRomanStep.tupled).reduce {
      (acc, f) => acc.orElse(f)
    }

  def roman(i: Int): String = {
    @annotation.tailrec
    def loop(remainingValue: Int, acc: StringBuilder): String =
      if (remainingValue == 0)
        acc.result()
      else {
        val (removedValue, newToken) = tryRoman(remainingValue)
        loop(remainingValue - removedValue, acc.append(newToken))
      }

    loop(
      remainingValue = i,
      acc = new StringBuilder()
    )
  }
}

As per Luis, "the chain of functions is built and reduced just one time."

Obviously this is already an overengineered toy with far more complexity than the problem requires, but I could (vaguely) imagine some marginally similiar scenario where calculating the partial funciton is less trivial.

Would it be possible to modify this function so only as much of the partial function would be built as necessary to resolve the input, but also whatever has been built will be saved for future use?

For example, if the first invocation provides the input of "1,000", the second invocation of the function would already "know" that this resolves as "M", but it could then still proceed to calculate (for example) that "100" returns "C", at which point the third invocation would know only so much but could still go on to resolve more... and the whole partial function wouldn't be "known" until the a value less than 4 is provided?

0

There are 0 best solutions below