Параметризация типов в Scala

Итак, я сейчас изучаю Scala и пытаюсь создать абстрактный векторный класс с векторным пространством 3 (координаты x, y, z). Я пытаюсь добавить два из этих векторов вместе со следующим кодом:


package math

class Vector3[T](ax:T,ay:T,az:T) {
  def x = ax
  def y = ay
  def z = az
  override def toString = "<"+x+", "+y+", "+z+">"
  def add(that: Vector3[T]) = new Vector3(x+that.x, y+that.y, z+that.z)
}

Проблема в том, что я продолжаю получать эту ошибку:

ошибка: несоответствие типа;
найдено: T
требуется: строка
def add(that: Vector3[T]) = new Vector3(x+that.x, y+that.y, z +это.z)

Я попытался закомментировать метод "toString" выше, но, похоже, это не имеет никакого эффекта. Может ли кто-нибудь сказать мне, что я делаю неправильно?


person horatius83    schedule 14.05.2010    source источник


Ответы (4)


Используя Scala 2.8, вы можете написать:

case class Vector3[T: Numeric](val x: T, val y: T, val z: T) {
  override def toString = "(%s, %s, %s)" format (x, y, z)

  def add(that: Vector3[T]) = new Vector3(
    plus(x, that.x),
    plus(y, that.y),
    plus(z, that.z)
  )

  private def plus(x: T, y: T) = implicitly[Numeric[T]] plus (x, y)
}

Позволь мне объяснить. Во-первых, T: Numeric — это связанный контекст, который неявно предоставляет экземпляр Numeric[T] вашему классу.

Черта Numeric[T] обеспечивает операции над числовыми типами,

trait Numeric[T] extends Ordering[T] {
  def plus(x: T, y: T): T
  def minus(x: T, y: T): T
  def times(x: T, y: T): T
  def negate(x: T): T
  // other operations omitted
}

Выражение implicitly[Numeric[T]] извлекает этот неявный контекст, так что вы можете выполнять операции, такие как plus, над вашими конкретными аргументами x, y и z, как показано в приватном методе выше.

Теперь вы можете создавать и add различные экземпляры Vector3, такие как Int и Double:

scala> Vector3(1,2,3) add Vector3(4,5,6)                                  
res1: Vector3[Int] = (5, 7, 9)

scala> Vector3(1.1, 2.2, 3.3) add Vector3(4.4, 5.5, 6.6)                      
res2: Vector3[Double] = (5.5, 7.7, 9.899999999999999)

Боковое примечание: можно использовать неявные преобразования для преобразования значений в экземпляры Numeric[T].Ops, чтобы вместо этого можно было написать следующее:

  def add(that: Vector3[T]) = new Vector3(x + that.x, y + that.y, z + that.z)

Я намеренно решил не использовать эти неявные преобразования, поскольку они (могут) привести к некоторому снижению производительности из-за создания временных объектов-оболочек. Фактическое влияние на производительность зависит от JVM (например, в какой степени она поддерживает анализ побега, чтобы избежать фактического размещения объектов в куче). Использование привязки к контексту и implicitly позволяет избежать этих потенциальных накладных расходов... за счет некоторого многословия.

person Alex Boisvert    schedule 16.05.2010

Вы не наложили ограничения на параметр типа T, поэтому компилятор возвращается к интерпретации + как конкатенации строк.

person Randall Schulz    schedule 14.05.2010

Проблема T. Это тип Any, но Any не имеет оператора +. Ошибка о String немного отсутствует. Таким образом, вам придется определить минимальную привязку к типу, который это делает.

person sblundy    schedule 14.05.2010
comment
В версии 2.8 это Ops, по крайней мере, мне так сказали. Я понятия не имею, что делать в 2.7, но я новичок в Scala. - person erisco; 14.05.2010

Оба ответа от @sblundy и @Randall Schulz, конечно, верны, но если вам нужен более конкретный совет о том, как ограничить T, то как насчет:

class Vector3[T <% Double](ax:T,ay:T,az:T) {
...
}
person pdbartlett    schedule 15.05.2010