2016-02-11 2 views
1

я следующая функция, которая генерирует Равномерное распределяется значение между 2 границами:Используя двойное значение в методе дробного [T]

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) 
} 

и Ограниченность определяется следующим образом:

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 
    } 
} 

Тем не менее, я хочу Uniform работать на всех Fractional[T] значений, так что я хотел бы добавить контекст, связанный:

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) 
} 

Это срабатывает при выполнении Uniform[Double](x: Bounded[Double]), но другие невозможны и получают исключение ClassCastException во время выполнения, потому что их нельзя отличить. Есть ли способ решить это?

+0

Сколько различных типов 'T' вы ожидаете? Если это ограниченный набор, вы можете рассмотреть возможность создания функции nextRandom [T]. Это работает только в том случае, если вы знаете, какого рода 'T' ожидать. –

ответ

3

Я предложил бы определить новый класс типов, характеризующий типы, которые вы можете получить случайные экземпляры:

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  
} 

Теперь вы можете написать Uniform так:

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) 
} 

И использовать его как это:

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] 

Есть библиотеки, такие как rng, что pr овидайте класс подобного типа для вас, но они, как правило, ориентированы на чисто функциональные способы работы со случайными числами, поэтому, если вы хотите что-то более простое, вам, вероятно, лучше всего писать свои собственные.

+1

Очень красивый и элегантный ответ! Спасибо. Я снова кое-что узнал сегодня :-) – avanwieringen

+0

У меня вопрос: почему вы используете пропуск для имени (=>) для функции? Вы также можете сделать его «a: A» и передать функцию следующим образом: 'instance (Random.nextDouble)'. Это верно? – avanwieringen

+0

@avanwieringen Если вы сделали это «a: A», вы не получили бы функцию нулевой арности, вы получили бы значение, которое было бы результатом применения этой функции один раз. '=> A' необходим, если вам действительно нужны разные значения для' A'. :) –

Смежные вопросы