Search code examples
scalamaxminscala-option

Best way to score current extremum in collection type


I’m currently a little tired so I might be missing the obvious.

I have a var _minVal: Option[Double], which shall hold the minimal value contained in a collection of Doubles (or None, if the collection is empty)

When adding a new item to the collection, I have too check if _minVal is either None or greater than the new item (=candidate for new mimimum).

I’ve gone from

_minVal = Some(_minVal match {
    case Some(oldMin) => if (candidate < oldMin) candidate
                         else                    oldMin
    case None         =>                         candidate
})

(not very DRY) to

_minVal = Some(min(_minVal getOrElse candidate, candidate))

but still think I might be missing something…


Solution

  • Without Scalaz, you are going to pay some RY. But I'd write it as:

    _minVal = _minVal map (candidate min) orElse Some(candidate)
    

    EDIT

    Eric Torreborre, of Specs/Specs2 fame, was kind enough to pursue the Scalaz solution that has eluded me. Being a testing framework guy, he wrote the answer in a testing format, instead of the imperative, side-effecting original. :-)

    Here's the version using _minVal, Double instead of Int, side-effects, and some twists of mine now that Eric has done the hard work.

    // From the question (candidate provided for testing purposes)
    var _minVal: Option[Double] = None
    def candidate = scala.util.Random.nextDouble
    
    // A function "min"
    def min = (_: Double) min (_: Double)
    
    // A function "orElse"
    def orElse = (_: Option[Double]) orElse (_: Option[Double])
    
    // Extract function to decrease noise
    def updateMin = _minVal map min.curried(_: Double)
    
    // This is the Scalaz vesion for the above -- type inference is not kind to it
    // def updateMin = (_minVal map min.curried).sequence[({type lambda[a] = (Double => a)})#lambda, Double]
    
    // Say the magic words
    import scalaz._
    import Scalaz._   
    
    def orElseSome = (Option(_: Double)) andThen orElse.flip.curried
    def updateMinOrSome = updateMin <*> orElseSome
    
    // TAH-DAH!
     _minVal = updateMinOrSome(candidate)