简体   繁体   中英

Using a double value in a Fractional[T] method

I have the following function which generates a Uniform distributed value between 2 bounds:

def Uniform(x: Bounded[Double], n: Int): Bounded[Double] = {
    val y: Double = (x.upper - x.lower) * scala.util.Random.nextDouble() + x.lower
    Bounded(y, x.bounds)
}

and Bounded is defined as follows:

trait Bounded[T] {
  val underlying: T
  val bounds: (T, T)

  def lower: T = bounds._1
  def upper: T = bounds._2

  override def toString = underlying.toString + " <- [" + lower.toString + "," + upper.toString + "]"
}

object Bounded {
  def apply[T : Numeric](x: T, _bounds: (T, T)): Bounded[T] = new Bounded[T] {
    override val underlying: T = x
    override val bounds: (T, T) = _bounds
  }
}

However, I want Uniform to work on all Fractional[T] values so I wanted to add a context bound:

def Uniform[T : Fractional](x: Bounded[T], n: Int): Bounded[T] = {
    import Numeric.Implicits._
    val y: T = (x.upper - x.lower) * scala.util.Random.nextDouble().asInstanceOf[T] + x.lower
    Bounded(y, x.bounds)
}

This works swell when doing a Uniform[Double](x: Bounded[Double]) , but the other ones are impossible and get a ClassCastException at runtime because they can not be casted. Is there a way to solve this?

I'd suggest defining a new type class that characterizes types that you can get random instances of:

import scala.util.Random

trait GetRandom[A] {
  def next(): A
}

object GetRandom {
  def instance[A](a: => A): GetRandom[A] = new GetRandom[A] {
    def next(): A = a
  }

  implicit val doubleRandom: GetRandom[Double] = instance(Random.nextDouble())
  implicit val floatRandom: GetRandom[Float] = instance(Random.nextFloat())
  // Define any other instances here     
}

Now you can write Uniform like this:

def Uniform[T: Fractional: GetRandom](x: Bounded[T], n: Int): Bounded[T] = {
  import Numeric.Implicits._
  val y: T = (x.upper - x.lower) * implicitly[GetRandom[T]].next() + x.lower
  Bounded(y, x.bounds)
}

And use it like this:

scala> Uniform[Double](Bounded(2, (0, 4)), 1)
res15: Bounded[Double] = 1.5325899033654382 <- [0.0,4.0]

scala> Uniform[Float](Bounded(2, (0, 4)), 1)
res16: Bounded[Float] = 0.06786823 <- [0.0,4.0]

There are libraries like rng that provide a similar type class for you, but they tend to be focused on purely functional ways to work with random numbers, so if you want something simpler you're probably best off writing your own.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM