Я новичок в Scala и создаю инструменты для статистической оценки. Рассмотрим следующее: определена черта probabilityDistribution
, которая гарантирует, что классы, которые наследуют от нее, смогут выполнять определенные функции, такие как вычисление плотности. Два таких примера распределения вероятностей могут быть биномиальным и бета-распределением. Поддержка этих двух функций - Int
и Double
, соответственно.Характеристика с абстрактным типом метода Аргумент
Настройка
trait probabilityDistribution extends Serializable {
type T
def density(x: T): Double
}
case class binomial(n: Int, p: Double) extends probabilityDistribution {
type T = Int
def density(x: Int): Double = x*p
}
case class beta(alpha: Double, beta: Double) extends probabilityDistribution {
type T = Double
def density(x: Double): Double = x*alpha*beta
}
Обратите внимание, что фактические математические реализации этих density
методов упрощенным выше. Теперь рассмотрим модель смеси, в которой у нас есть несколько функций или переменных, которые поступают из разных распределений. Мы можем создать список probabilityDistribution
s для представления наших функций.
val p = List(binomial(5, .5), beta(.5,.5))
Предположит, что теперь мы заинтересованы в поставках вектора гипотетических значений данных, и хочет запросить density
функции для каждого соответствующего распределения вероятностей.
val v = List[Any](2, 0.75)
Проблема Конечно, мы используем молнию с картой. Однако, это не работает:
p zip v map { case (x,y) => x.density(y) }
### found : Any
# required: x.T
Оговорка: Выбор контейнера Действительный вопрос заключается задаваться вопросом, почему я выбрал List[Any]
в качестве контейнера для хранения значений данных, а не List[Double]
, или, возможно, List[T <: Double]
. Рассмотрим случай, когда некоторые из наших вероятностных распределений имеют поддержку над векторами или даже матриц (например, многомерное нормальное и обратное Уишарта)
Идея обратиться предостережение может быть вместо вмещать наши входные значения в контейнере, который является более представительный тип ввода. например что-то вроде
class likelihoodSupport
val v = List[likelihoodSupport](...)
где Int
, Double
и Array[Double]
и даже кортеж (Array[Double], Array[Array[Double]])
все унаследует от likelihoodSupport
. Однако, поскольку некоторые из этих классов являются окончательными, это невозможно.
Один (вшивом) Фикс
Обратите внимание, что это может быть обработан с помощью сопоставления с образцом и полиморфный метод в пределах каждого подкласса, но, как Одерски может сказать это имеет код запах:
trait probabilityDistribution extends Serializable {
type T
def density[T](x: T): Double
}
case class binomial(n: Int, p: Double) extends probabilityDistribution {
type T = Int
def density[U](x: U): Double = x match {case arg: Int => arg * p }
}
case class beta(alpha: Double, beta: Double) extends probabilityDistribution {
type T = Double
def density[U](x: U): Double = x match {case arg: Double => arg * alpha * beta}
}
Теперь мы можем запустить
p zip v map { case (x,y) => x.density(y) }
Plea Я знаю, что я пытаюсь сделать очень легко на таком красивом и мощном языке, но я не могу понять, как это сделать! Ваша помощь очень ценится.
Примечание Я не заинтересован в использовании дополнительных упаковок/импортов, поскольку, по моему мнению, эту проблему необходимо решить тривиально в базе Scala.
Почему бы просто не использовать простые дженерики 'ProbabilityDistribution [T]'? –
Для списка с известными различными типами элементов существует 'HList' (например, в бесформенном виде). –
Я понимаю, что «простые дженерики» ничего не покупают; использование абстрактного типа в определении признака эквивалентно в этом случае. Кроме того, я не заинтересован в импорте дополнительных пакетов, так как я считаю, что эта проблема должна быть тривиально решена в базовой scala. – Andreas