首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >方法论证中的抽象型特征

方法论证中的抽象型特征
EN

Stack Overflow用户
提问于 2015-09-11 05:35:06
回答 1查看 112关注 0票数 2

我对Scala很陌生,并且正在构建用于统计估计的工具。请考虑以下几点:定义了一个特性probabilityDistribution,它保证继承它的类能够执行某些功能,例如计算密度。概率分布的两个例子可能是二项式分布和β分布。这两个函数的支持分别是IntDouble

设置

代码语言:javascript
复制
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列表来表示我们的特性。

代码语言:javascript
复制
val p = List(binomial(5, .5), beta(.5,.5))

假设我们现在对提供假设数据值的向量感兴趣,并希望查询每个概率分布的density函数。

代码语言:javascript
复制
val v = List[Any](2, 0.75)

问题,当然,我们在地图中使用zip。然而,这是行不通的:

代码语言:javascript
复制
p zip v map { case (x,y) => x.density(y) }
### found   : Any
  # required: x.T

警告:容器的选择一个有效的问题是,为什么我选择List[Any]作为容器来保存数据值,而不是List[Double],或者List[T <: Double]。考虑一下当我们的一些概率分布对向量或甚至矩阵有支持时(例如多元正态和逆Wishart)。

--一个解决警告的想法可能是将我们的输入值放在一个更能代表我们输入类型的容器中。例如,类似于

代码语言:javascript
复制
class likelihoodSupport
val v = List[likelihoodSupport](...)

其中IntDoubleArray[Double]甚至元组(Array[Double], Array[Array[Double]])都继承自likelihoodSupport。然而,由于其中一些类是最终的,所以这是不可能的。

One (Crummy) Fix

请注意,这可以通过在每个子类中使用模式匹配和多态方法来处理,但是正如Odersky可能说的那样,这有一个代码气味。

代码语言:javascript
复制
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}
}

我们现在可以跑了

代码语言:javascript
复制
p zip v map { case (x,y) => x.density(y) }

--我知道我想做什么--应该很容易用这样一种美丽而强大的语言来完成,但是我不知道怎么做到的!非常感谢你的帮助。

备注我对使用其他包/导入不感兴趣,因为我觉得这个问题应该在base中解决。

EN

回答 1

Stack Overflow用户

发布于 2015-09-11 06:20:42

如果有单独的pv列表(至少没有强制转换,或者编写自己的HList库),您就不能这样做。这一点应该很明显:如果您更改了其中一个列表中元素的顺序,类型就不会改变(与HList不同),但是发行版现在将与错误类型的值配对!

最简单的方法是添加一个强制转换:

代码语言:javascript
复制
p zip v map { case (x,y) => x.density(y.asInstanceOf[x.T]) }

请注意,由于JVM类型擦除,这可能是运行时的不操作,并导致density调用中的density

如果您想要更安全地替代强制转换,应该可以使用类似的方法(有关ClassTags和相关类型的更多信息,请参见ClassTags):

代码语言:javascript
复制
// note that generics do buy you some convenience in this case:
// abstract class probabilityDistribution[T](implicit val tag: ClassTag[T]) extends Serializable
// will mean you don't need to set tag explicitly in subtypes
trait probabilityDistribution extends Serializable {
  type T
  implicit val tag: ClassTag[T]
  def density(x: T): Double
}

case class binomial(n: Int, p: Double) extends probabilityDistribution {
  type T = Int
  val tag = classTag[Int]
  def density(x: Int): Double = x*p
}

p zip v map { (x,y) => 
  implicit val tag: ClassTag[x.T] = x.tag
  y match { 
    case y: x.T => ... 
    case _ => ... 
  }
}

或者可以组合分布和值(或包含值的数据结构、返回值的函数等):

代码语言:javascript
复制
// alternately DistribWithValue(d: probabilityDistribution)(x: d.T)
case class DistribWithValue[A](d: probabilityDistribution { type T = A }, x: A) {
  def density = d.density(x)
}

val pv: List[DistribWithValue[_]] = List(DistribWithValue(binomial(5, .5), 2), DistribWithValue(beta(.5,.5), 0.75))

// if you want p and v on their own
val p = pv.map(_.d)
val v = pv.map(_.x)

当然,如果您想使用probabilityDistribution作为方法参数,正如问题标题所述,这很简单,例如:

代码语言:javascript
复制
def density(d: probabilityDistribution)(xs: List[d.T]) = xs.map(d.density _)

问题只有在下列情况下才会出现:

用户可能希望使用与概率分布本身本质无关的不同x值进行多个密度查询。

编译器无法证明这些值具有正确的类型。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32516168

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档