Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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…

like image 675
flying sheep Avatar asked Aug 12 '11 20:08

flying sheep


2 Answers

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)
like image 162
Daniel C. Sobral Avatar answered Oct 03 '22 09:10

Daniel C. Sobral


Here is an update to Daniel's answer, using Scalaz:

Here's a curried 'min' function:

def min = (i: Int) => (j: Int) => if (i < j) i else j 

And 2 variables:

// the last minimum value
def lastMin: Option[Int] = None

// the new value
def current = 1

Now let's define 2 new functions

// this one does the minimum update
def updateMin = (i: Int) => lastMin map (min(i))

// this one provides a default value if the option o2 is not defined
def orElse = (o1: Int) => (o2: Option[Int]) => o2 orElse Some(o1)

Then using the excellent explanation by @dibblego of why Function1[T, _] is an applicative functor, we can avoid the repetition of the 'current' variable:

(updateMin <*> orElse).apply(current) === Some(current)
like image 27
Eric Avatar answered Oct 03 '22 11:10

Eric