Выполнение универсальных / производительных примитивов в scala на самом деле включает в себя два связанных механизма, которые scala использует для избежания упаковки / распаковки (например, упаковка int
в java.lang.Integer
и наоборот):
@specialize
тип аннотации
- Использование
Manifest
с массивами
specialize
- это аннотация, которая сообщает компилятору Java о создании «примитивных» версий кода (как я уже говорил, в шаблонах C ++). Проверьте объявление типа Tuple2
(которое является специализированным) по сравнению с List
(которое не является). Он был добавлен в 2.8 и означает, что, например, код, подобный CC[Int].map(f : Int => Int)
, выполняется без каких-либо ограничений int
s (при условии, что CC
специализирован, конечно!).
Manifest
s - это способ создания улучшенных типов в scala (что ограничено стиранием типа JVM ). Это особенно полезно, когда вы хотите иметь обобщенный метод для некоторого типа T
, а затем создать массив T
(т.е. T[]
) внутри метода. В Java это невозможно, потому что new T[]
недопустимо. В Scala это возможно с помощью Manifests. В частности, и в этом случае это позволяет нам построить примитив T-массив, такой как double[]
или int[]
. (Это круто, если вам интересно)
Бокс так важен с точки зрения производительности, потому что он создает мусор, если только все ваши int
не <127. Он также, очевидно, добавляет уровень косвенности с точки зрения дополнительных шагов процесса / вызовов методов и т.д. Но учтите, что вы, вероятно, не будете кричать, если не будете абсолютно уверены в том, что определенно это делаете (т.е. большая часть кода не нуждается в такой микрооптимизации) </em>
Итак, вернемся к вопросу: для того, чтобы сделать это без упаковки / распаковки, вы должны использовать Array
(List
еще не специализирован, и в любом случае будет более требовательным к объектам, даже если бы он был! ). Функция zipped
для пары коллекций возвращает коллекцию Tuple2
с (для которой не требуется бокс, поскольку специализируется на ).
Для того, чтобы сделать это в общем (то есть для различных числовых типов), вы должны требовать контекст, связанный с вашим универсальным параметром, чтобы он был Numeric
и чтобы можно было найти Manifest
(требуется для создания массива). Итак, я начал в том же духе ...
def abs[T : Numeric : Manifest](rs : Array[T], ims : Array[T]) : Array[T] = {
import math._
val num = implicitly[Numeric[T]]
(rs, ims).zipped.map { (r, i) => sqrt(num.plus(num.times(r,r), num.times(i,i))) }
// ^^^^ no SQRT function for Numeric
}
... но это не совсем работает . Причина в том, что «универсальное» значение Numeric
не имеет такой операции, как sqrt
->, поэтому вы можете сделать это только в том случае, если знаете, что у вас есть Double
. Например:
scala> def almostAbs[T : Manifest : Numeric](rs : Array[T], ims : Array[T]) : Array[T] = {
| import math._
| val num = implicitly[Numeric[T]]
| (rs, ims).zipped.map { (r, i) => num.plus(num.times(r,r), num.times(i,i)) }
| }
almostAbs: [T](rs: Array[T],ims: Array[T])(implicit evidence$1: Manifest[T],implicit evidence$2: Numeric[T])Array[T]
Отлично - теперь посмотрите, как этот чисто общий метод делает что-то!
scala> val rs = Array(1.2, 3.4, 5.6); val is = Array(6.5, 4.3, 2.1)
rs: Array[Double] = Array(1.2, 3.4, 5.6)
is: Array[Double] = Array(6.5, 4.3, 2.1)
scala> almostAbs(rs, is)
res0: Array[Double] = Array(43.69, 30.049999999999997, 35.769999999999996)
Теперь мы можем sqrt
результат, потому что у нас есть Array[Double]
scala> res0.map(math.sqrt(_))
res1: Array[Double] = Array(6.609841147864296, 5.481788029466298, 5.980802621722272)
И чтобы доказать, что это будет работать даже с другим Numeric
типом:
scala> import math._
import math._
scala> val rs = Array(BigDecimal(1.2), BigDecimal(3.4), BigDecimal(5.6)); val is = Array(BigDecimal(6.5), BigDecimal(4.3), BigDecimal(2.1))
rs: Array[scala.math.BigDecimal] = Array(1.2, 3.4, 5.6)
is: Array[scala.math.BigDecimal] = Array(6.5, 4.3, 2.1)
scala> almostAbs(rs, is)
res6: Array[scala.math.BigDecimal] = Array(43.69, 30.05, 35.77)
scala> res6.map(d => math.sqrt(d.toDouble))
res7: Array[Double] = Array(6.609841147864296, 5.481788029466299, 5.9808026217222725)