错误:协变类型 A 出现在逆变位置

发布于 2024-10-21 05:54:08 字数 2343 浏览 1 评论 0原文

我试图编写一个不可变的 Matrix[A] 类。我希望该类在 A 上是协变的,但是当我将 + 放在 A 前面时,编译器开始抱怨该类中的某些操作。

以下是我的 Matrix 类的相关子集(实际类比以下子集大 5 倍):

class Matrix[+A] private(val contents: Vector[Vector[A]])(implicit numericEv: Numeric[A])
    extends ((Int, Int) => A) with Proxy {

  import numericEv._
  import Prelude._

  // delegate `equals` and `hashCode` implementations to `contents`
  override def self = contents

  val nRows: Int = contents.length

  val nColumns: Int = contents(0).length.ensuring { len =>
    contents.forall(_.length == len)        
  }

  def dimensions = (nRows, nColumns)

  def isSquare = nRows == nColumns

  def hasSameOrderAs[B : Numeric](that: Matrix[B]) = this.dimensions == that.dimensions

  def isComformableWith[B : Numeric](that: Matrix[B]) = this.nColumns == that.nRows

  private def assertSameOrder[B : Numeric](that: Matrix[B]) {
    assert(this.hasSameOrderAs(that), "Matrices differ in dimensions.")
  }

  private def assertIsSquare() {
    assert(this.isSquare, "Not a square matrix.")
  }      

  def zipWith[B : Numeric, C : Numeric](that: Matrix[B])(f: (A, B) => C): Matrix[C] = {
    assertSameOrder(that)
    val zippedContents = (contents, that.contents).zipped.map((v1, v2) => (v1, v2).zipped.map(f))
    Matrix(zippedContents)
  }

  def map[B : Numeric](f: A => B): Matrix[B] = {
    Matrix(contents.map(_.map(f)))
  }

  def transpose: Matrix[A] = {
    assertIsSquare()
    Matrix(contents.transpose)
  }

  def +(that: Matrix[A]): Matrix[A] = this.zipWith(that)(_ + _)

  def -(that: Matrix[A]): Matrix[A] = this.zipWith(that)(_ - _)

  def *(scalar: A): Matrix[A] = this.map(_ * scalar)

  def *(that: Matrix[A]): Matrix[A] = {
    assert(this.isComformableWith(that))
    Matrix.tabulate(this.nRows, that.nColumns) { (r, c) =>
      (this(r), that.transpose(c)).zipped.map(_ * _).sum
    }
  }
}

object Matrix {
  def apply[A : Numeric](rows: Vector[A]*): Matrix[A] = Matrix(Vector(rows: _*))

  def apply[A : Numeric](contents: Vector[Vector[A]]): Matrix[A] = new Matrix(contents)

  def tabulate[A : Numeric](nRows: Int, nColumns: Int)(f: (Int, Int) => A): Matrix[A] = {
    Matrix(Vector.tabulate(nRows, nColumns)(f))
  }
}

对于最后四个操作,编译器显示错误“协变类型 A 出现在逆变位置”班级。我无法理解这些错误的原因以及如何消除它。请解释这些错误背后的原因并提出解决方法。谢谢。

I was trying to write an immutable Matrix[A] class. I want the class to be covariant on A but when I put + in front of A compiler starts complaining about some operations in the class.

Following is a relevant subset of my Matrix class (The actual class is some 5 times bigger than the following subset):

class Matrix[+A] private(val contents: Vector[Vector[A]])(implicit numericEv: Numeric[A])
    extends ((Int, Int) => A) with Proxy {

  import numericEv._
  import Prelude._

  // delegate `equals` and `hashCode` implementations to `contents`
  override def self = contents

  val nRows: Int = contents.length

  val nColumns: Int = contents(0).length.ensuring { len =>
    contents.forall(_.length == len)        
  }

  def dimensions = (nRows, nColumns)

  def isSquare = nRows == nColumns

  def hasSameOrderAs[B : Numeric](that: Matrix[B]) = this.dimensions == that.dimensions

  def isComformableWith[B : Numeric](that: Matrix[B]) = this.nColumns == that.nRows

  private def assertSameOrder[B : Numeric](that: Matrix[B]) {
    assert(this.hasSameOrderAs(that), "Matrices differ in dimensions.")
  }

  private def assertIsSquare() {
    assert(this.isSquare, "Not a square matrix.")
  }      

  def zipWith[B : Numeric, C : Numeric](that: Matrix[B])(f: (A, B) => C): Matrix[C] = {
    assertSameOrder(that)
    val zippedContents = (contents, that.contents).zipped.map((v1, v2) => (v1, v2).zipped.map(f))
    Matrix(zippedContents)
  }

  def map[B : Numeric](f: A => B): Matrix[B] = {
    Matrix(contents.map(_.map(f)))
  }

  def transpose: Matrix[A] = {
    assertIsSquare()
    Matrix(contents.transpose)
  }

  def +(that: Matrix[A]): Matrix[A] = this.zipWith(that)(_ + _)

  def -(that: Matrix[A]): Matrix[A] = this.zipWith(that)(_ - _)

  def *(scalar: A): Matrix[A] = this.map(_ * scalar)

  def *(that: Matrix[A]): Matrix[A] = {
    assert(this.isComformableWith(that))
    Matrix.tabulate(this.nRows, that.nColumns) { (r, c) =>
      (this(r), that.transpose(c)).zipped.map(_ * _).sum
    }
  }
}

object Matrix {
  def apply[A : Numeric](rows: Vector[A]*): Matrix[A] = Matrix(Vector(rows: _*))

  def apply[A : Numeric](contents: Vector[Vector[A]]): Matrix[A] = new Matrix(contents)

  def tabulate[A : Numeric](nRows: Int, nColumns: Int)(f: (Int, Int) => A): Matrix[A] = {
    Matrix(Vector.tabulate(nRows, nColumns)(f))
  }
}

Compiler shows the error "Covariant type A occurs in contravariant position" for the last four operations in the class. I am unable to understand the reason for these errors, and how to get rid of it. Please explain the reason behind these errors and suggest a way to work around them. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

送你一个梦 2024-10-28 05:54:08

出现这些错误的原因是,像您所做的那样声明它不是类型安全的。例如,可以这样做,否则:

class A(val x: Int)
class B(x: Int, val y: Int) extends A(x)

object NA extends Numeric[A] {
    def toDouble(x: A): Double = x.x.toDouble
    def toFloat(x: A): Float = x.x.toFloat
    def toLong(x: A): Long = x.x.toLong
    def toInt(x: A): Int = x.x
    def fromInt(x: Int): A = new A(x)
    def negate(x: A): A = new A(-x.x)
    def times(x: A,y: A): A = new A(x.x * y.x)
    def minus(x: A,y: A): A = new A(x.x - y.x)
    def plus(x: A,y: A): A = new A(x.x + y.x)
    def compare(x: A,y: A): Int = implicitly[Numeric[Int]].compare(x.x, y.x)
}

object NB extends Numeric[B] {
    def toDouble(x: B): Double = x.x.toDouble / x.y.toDouble
    def toFloat(x: B): Float = x.x.toFloat / x.y.toFloat
    def toLong(x: B): Long = (x.x / x.y).toLong
    def toInt(x: B): Int = x.x / x.y
    def fromInt(x: Int): B = new B(x, 1)
    def negate(x: B): B = new B(-x.x, x.y)
    def times(x: B,y: B): B = new B(x.x * y.x, x.y * y.y)
    def minus(x: B,y: B): B = new B(x.x * y.y - y.x * x.y, x.y * y.y)
    def plus(x: B,y: B): B = new B(x.x * y.y + y.x * x.y, x.y * y.y)
    def compare(x: B,y: B): Int = implicitly[Numeric[Int]].compare(x.x * x.y, y.x * y.y)
}

val mb = Matrix.tabulate(10, 10)((x, y) => new B(x, y))
def f(m: Matrix[A]) = {
  val ma = Matrix.tabulate(m.nRows, m.nColumns)((x, y) => 1)
  m + ma
}
f(mb)

请注意,m + ma 无法工作,因为 m.+ 需要一个 B< 类型的对象/代码>。不过,如果 Scala 允许您按照自己的方式编写,那么这是允许的。

避免这个问题的常见方法是这样编写方法:

def +[B >: A](that: Matrix[B])(implicit num: Numeric[B]): Matrix[B] = this.zipWith(that)(B.plus)

The reason for these errors is that it is not type-safe to declare it as you are doing. For instance, it would be possible to do this, otherwise:

class A(val x: Int)
class B(x: Int, val y: Int) extends A(x)

object NA extends Numeric[A] {
    def toDouble(x: A): Double = x.x.toDouble
    def toFloat(x: A): Float = x.x.toFloat
    def toLong(x: A): Long = x.x.toLong
    def toInt(x: A): Int = x.x
    def fromInt(x: Int): A = new A(x)
    def negate(x: A): A = new A(-x.x)
    def times(x: A,y: A): A = new A(x.x * y.x)
    def minus(x: A,y: A): A = new A(x.x - y.x)
    def plus(x: A,y: A): A = new A(x.x + y.x)
    def compare(x: A,y: A): Int = implicitly[Numeric[Int]].compare(x.x, y.x)
}

object NB extends Numeric[B] {
    def toDouble(x: B): Double = x.x.toDouble / x.y.toDouble
    def toFloat(x: B): Float = x.x.toFloat / x.y.toFloat
    def toLong(x: B): Long = (x.x / x.y).toLong
    def toInt(x: B): Int = x.x / x.y
    def fromInt(x: Int): B = new B(x, 1)
    def negate(x: B): B = new B(-x.x, x.y)
    def times(x: B,y: B): B = new B(x.x * y.x, x.y * y.y)
    def minus(x: B,y: B): B = new B(x.x * y.y - y.x * x.y, x.y * y.y)
    def plus(x: B,y: B): B = new B(x.x * y.y + y.x * x.y, x.y * y.y)
    def compare(x: B,y: B): Int = implicitly[Numeric[Int]].compare(x.x * x.y, y.x * y.y)
}

val mb = Matrix.tabulate(10, 10)((x, y) => new B(x, y))
def f(m: Matrix[A]) = {
  val ma = Matrix.tabulate(m.nRows, m.nColumns)((x, y) => 1)
  m + ma
}
f(mb)

Note that m + ma could not work, since m.+ expects an object of type B. If Scala allowed you to write it the way you did, though, this would be allowed.

The common way to avoid this problem is to write the method like this:

def +[B >: A](that: Matrix[B])(implicit num: Numeric[B]): Matrix[B] = this.zipWith(that)(B.plus)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文