Подтвердить что ты не робот

Как создать класс для всех числовых типов?

Я пытаюсь создать класс Vector, который является общим для всех числовых типов. моя первоначальная попытка состояла в том, чтобы написать класс для всех типов, подобных этому:

class Vector3f(val x:Float, val y:Float, val z:Float)

поскольку scala поддерживает специализированную аннотацию, которую я мог бы использовать для создания этих классов для всех числовых типов

class Vector3[A <: What?](val x:A,val y:A, val z:A)

но все, что я нашел как супер тип для чисел, было AnyVal, но AnyVal не поддерживает + - */. Итак, каков правильный способ сделать это, но не жертвуя производительностью распакованных типов номеров?

4b9b3361

Ответ 1

Вы не можете. Не прямо сейчас. Может быть, когда, и если Numeric станет специализированным.

Предположим, что вы получаете самый простой параметризованный класс:

class Vector3[@specialized T](val x: T, val y: T, val z: T)(implicit num: Numeric[T]) {
    def +(other: Vector3[T]) = new Vector3(num.plus(x, other.x), num.plus(y, other.y), num.plus(z, other.z))
}

Метод + будет скомпилирован примерно так:

override <specialized> def +$mcD$sp(other: Vector3): Vector3 = new Vector3$mcD$sp(
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.x()), 
      scala.Double.box(other.x$mcD$sp()))),
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.y()),
      scala.Double.box(other.y$mcD$sp()))),
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.z()), 
      scala.Double.box(other.z$mcD$sp()))), 
  Vector3$mcD$sp.this.Vector3$$num);

Этот вывод scalac -optimize -Xprint:jvm. Теперь для каждого специализированного типа есть даже подклассы, поэтому вы можете инициализировать Vector3 без бокса, но пока Numeric не специализируется, вы не можете идти дальше.

Ну... вы можете написать свой собственный Numeric и специализироваться на этом, но в этот момент я не уверен, что вы набираете, сделав класс параметризованным на первом месте.

Ответ 2

Короткий ответ: вы не можете получить полную производительность. Или, по крайней мере, я не нашел ничего, что дает полную производительность. (И я пробовал какое-то время именно в этом случае, я отказался и вместо этого написал генератор кода, тем более, что вы не можете обрабатывать разные размеры векторов в целом.)

Я был бы рад, если бы мне было показано иначе, но до сих пор все, что я пробовал, было небольшим (30%) до огромного (900%) увеличения времени выполнения.


Изменить: вот тест, показывающий, что я имею в виду.

object Specs {
  def ptime[T](f: => T): T = {
    val t0 = System.nanoTime
    val ans = f
    printf("Elapsed: %.3f s\n",(System.nanoTime-t0)*1e-9)
    ans
  }
  def lots[T](n: Int, f: => T): T = if (n>1) { f; lots(n-1,f) } else f

  sealed abstract class SpecNum[@specialized(Int,Double) T] {
    def plus(a: T, b: T): T
  }

  implicit object SpecInt extends SpecNum[Int] {
    def plus(a: Int, b: Int) = a + b
  }

  final class Vek[@specialized(Int,Double) T](val x: T, val y: T) {
    def +(v: Vek[T])(implicit ev: SpecNum[T]) = new Vek[T](ev.plus(x,v.x), ev.plus(y,v.y))
  }

  final class Uek[@specialized(Int,Double) T](var x: T, var y: T) {
    def +=(u: Uek[T])(implicit ev: SpecNum[T]) = { x = ev.plus(x,u.x); y = ev.plus(y,u.y); this }
  }

  final class Veq(val x: Int, val y: Int) {
    def +(v: Veq) = new Veq(x + v.x, y + v.y)
  }

  final class Ueq(var x: Int, var y: Int) {
    def +=(u: Ueq) = { x += u.x; y += u.y; this }
  }

  def main(args: Array[String]) {
    for (i <- 1 to 6) {
      ptime(lots(1000000,{val v = new Vek[Int](3,5); var u = new Vek[Int](0,0); var i=0; while (i<100) { u = (u+v); i += 1 }; u}))
      ptime(lots(1000000,{val v = new Veq(3,5); var u = new Veq(0,0); var i=0; while (i<100) { u = (u+v); i += 1 }; u}))
      ptime(lots(1000000,{val v = new Uek[Int](3,5); val u = new Uek[Int](0,0); var i=0; while (i<100) { u += v; i += 1 }; u}))
      ptime(lots(1000000,{val v = new Ueq(3,5); val u = new Ueq(0,0); var i=0; while (i<100) { u += v; i += 1 }; u}))
    }
  }
}

и вывод:

Elapsed: 0.939 s
Elapsed: 0.535 s
Elapsed: 0.077 s
Elapsed: 0.075 s
Elapsed: 0.947 s
Elapsed: 0.352 s
Elapsed: 0.064 s
Elapsed: 0.063 s
Elapsed: 0.804 s
Elapsed: 0.360 s
Elapsed: 0.064 s
Elapsed: 0.062 s
Elapsed: 0.521 s  <- Immutable specialized with custom numeric
Elapsed: 0.364 s  <- Immutable primitive type
Elapsed: 0.065 s  <- Mutable specialized with custom numeric
Elapsed: 0.065 s  <- Mutable primitive type
...