通用微风矢量方法

时间:2015-08-10 12:17:59

标签: scala generics vector scala-breeze

我正在尝试实现一个通用的Scala方法,该方法处理类型为Float或Double的Breeze向量(至少是一个加号更少的特异性)。 这是Vector [Double]的一个简单示例:

def vectorSum(vectors: Seq[Vector[Double]]): Vector[Double] = {
  vectors.reduce { (v1, v2) => v1 :+ v2 }
}

我对Scala和Breeze稍微有点新意,所以我天真的做法是:

def vectorSumGeneric[T <: AnyVal](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce { (v1, v2) => v1 :+ v2 }
}

但是,这会引发以下编译错误:

  • diverging implicit expansion for type breeze.linalg.operators.OpAdd.Impl2[breeze.linalg.Vector[T],breeze.linalg.Vector[T],That] starting with method v_v_Idempotent_OpAdd in trait VectorOps
  • not enough arguments for method :+: (implicit op: breeze.linalg.operators.OpAdd.Impl2[breeze.linalg.Vector[T],breeze.linalg.Vector[T],That])That. Unspecified value parameter op.

我尝试了一些变体,包括T <% AnyValT <% Double,但它们也不起作用(可能是预期的)。键入边界的Scala文档并没有给我一个关于这样的用例的线索。 解决这个问题的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

问题是类型参数T可以是任何类型,但您必须确保类型T至少支持添加作为代数运算。如果T是半环,那么您可以添加两个T类型的元素。您可以通过指定上下文绑定来强制T成为半环境:

def vectorSum[T: Semiring](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce(_ + _)
}

这样你就可以强制执行T的每个实例化,你的范围内也有一个Semiring[T]来定义添加操作。 Breeze已经为所有支持添加的原始类型定义了这个结构。

如果你想支持更多代数运算,例如除法,那么你应该约束你的类型变量以使Field上下文绑定。

def vectorDiv[T: Field](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce(_ / _)
}

如果要支持向量上的通用元素二进制运算:

def vectorBinaryOp[T](
    vectors: Seq[Vector[T]], op: (T, T) => T)(
    implicit canZipMapValues: CanZipMapValues[Vector[T], T, T, Vector[T]])
  : Vector[T] = {
  vectors.reduce{
    (left, right) => implicitly[CanZipMapValues[Vector[T], T, T, Vector[T]]].map(left, right, op)
  }
}

然后你可以在向量上定义任意二进制操作:

val vectors = Seq(DenseVector(1.0,2.0,3.0,4.0), DenseVector(2.0,3.0,4.0,5.0))
val result = VectorSum.vectorBinaryOp(vectors, (a: Double, b: Double) => (a / b))