1

我用子类 Complex 和 IntCombiner 创建了 Combiner 特征,我的目标是让 Matrix 与 Complex 和 Int 一起工作。但是由于某种原因它没有编译说

[com.implicits.TestImplicits1.IntCombiner] do not conform to class Matrix's type parameter bounds [T <: com.implicits.TestImplicits1.Combiner[T]]
    val m1 = new Matrix[IntCombiner](3, 3)((1 to 9).sliding(3).map {

但据我了解,IntContainer 是 Combiner 的子类,它应该可以工作。为什么会出现这样的错误请解释?

object TestImplicits1 {

  trait Combiner[T] {

    def +(b: T): T

    def *(b: T): T

  }

  class Complex(r: Double, i: Double) extends Combiner[Complex] {

    val real = r

    val im = i

    override def +(b: Complex): Complex = {
      new Complex(real + b.real, im + b.im)
    }

    override def *(b: Complex): Complex = {
      new Complex((real * b.real) - (im * b.im), real * b.im + b.real * im)
    }

  }

  class IntCombiner(a: Int) extends Combiner[Int] {
    val v = a

    override def *(b: Int): Int = v * b

    override def +(b: Int): Int = v + b

  }

  class Matrix[T <: Combiner[T]](x1: Int, y1: Int)(ma: Seq[Seq[T]]) {

    self =>
    val x: Int = x1
    val y: Int = y1

    def dot(v1: Seq[T], v2: Seq[T]): T = {
      v1.zip(v2).map { t: (T, T) => {
        t._1 * t._2
      }
      }.reduce(_ + _)
    }

  }

  object MatrixInt extends App {
    def apply[T <: Combiner[T]](x1: Int, y1: Int)(s: Seq[Seq[T]]) = {
      new Matrix[T](x1, y1)(s)
    }

    val m1 = new Matrix[IntCombiner](3, 3)((1 to 9).sliding(3).map {
      x => x map { y => new IntCombiner(y) }
    }.toSeq)
  }

}
4

1 回答 1

2

F-bounded polymorphism 不能添加到现有Int类中,因为Int它就是这样,它对你的特征一无所知Combiner,所以它不能扩展Combiner[Int]。你可以将每个都包装Int成类似的东西IntWrapper <: Combiner[IntWrapper],但这会浪费相当多的内存,并且围绕 F 有界多态性的库设计往往很棘手。

这是一个基于临时多态性和类型类的提案:

object TestImplicits1 {

  trait Combiner[T] {
    def +(a: T, b: T): T
    def *(a: T, b: T): T
  }

  object syntax {
    object combiner {
      implicit class CombinerOps[A](a: A) {
        def +(b: A)(implicit comb: Combiner[A]) = comb.+(a, b)
        def *(b: A)(implicit comb: Combiner[A]) = comb.*(a, b)
      }
    }
  }

  case class Complex(re: Double, im: Double)

  implicit val complexCombiner: Combiner[Complex] = new Combiner[Complex] {

    override def +(a: Complex, b: Complex): Complex = {
      Complex(a.re + b.re, a.im + b.im)
    }

    override def *(a: Complex, b: Complex): Complex = {
      Complex((a.re * b.re) - (a.im * b.im), a.re * b.im + b.re * a.im)
    }

  }

  implicit val intCombiner: Combiner[Int] = new Combiner[Int] {
    override def *(a: Int, b: Int): Int = a * b
    override def +(a: Int, b: Int): Int = a + b
  }

  class Matrix[T: Combiner](entries: Vector[Vector[T]]) {
    def frobeniusNormSq: T = {
      import syntax.combiner._
      entries.map(_.map(x => x * x).reduce(_ + _)).reduce(_ + _)
    }
  }
}    

我不知道你在那里尝试了什么dot,你的x1,x2并且ma似乎完全没有使用,所以我添加了一个简单的 Frobenius 范数平方示例,只是为了展示类型类和运算符的语法糖如何协同工作。请不要期望它有任何类似于“高性能”的东西 - JVM 传统上从不关心矩形数组和数字运算(至少不在单个计算节点上;Spark & Co 是另一回事)。至少您的代码不会自动转换为优化的 CUDA 代码,这是肯定的。

于 2019-01-12T16:15:55.480 回答