如何使用Scala专业化提供手动专用实现?
专业化有望为原始类型提供高效率的实现
最少的额外样板。但专业化似乎过于渴望自己的利益。 如果我想专门化一个类或方法, def foo[@specialized(Byte) A](a: A): String = ??? class Bar[@specialized(Int) B] { var b: B = ??? def baz: B = ??? } 那么我需要编写一个涵盖专业和通用案例的实现。 我可能会编写一个专门的类型类来正确地进行数学运算,但这并不是一样的 class Adder[@specialized(Byte) A] { def +(a1: A,a2: A): A = ??? } 此外,一旦我以这种方式创建类型类,我如何确保我的专门方法使用正确的类型类 有没有办法在没有宏的情况下做到这一点?使用宏更容易吗? 解决方法
到目前为止,这是我最好的尝试。它工作但实现不漂亮(即使结果是)。欢迎改进!
在类和方法级别都有一种无宏的方法来做到这一点,它确实涉及类型类 – 相当多的 手动专业课程 您手动专门化类的方式与手动为类提供任何类型的不同实现的方式相同: abstract class Bippy[@specialized(Int) B] { def b: B def next: Bippy[B] } class BippyInt(initial: Int) extends Bippy[Int] { private var myB: Int = initial def b: Int = myB def next = { myB += 1; this } } class BippyObject(initial: Object) extends Bippy[Object] { private var myB: Object = initial def b: B = myB def next = { myB = myB.toString; this } } 现在,如果我们只有一个专门的方法来挑选正确的实现,我们就完成了: object Bippy{ def apply[@specialized(Int) B](initial: B) = ??? // Now what? } 所以我们已经将我们提供自定义专业类和方法的问题转化为公正 手动专业方法 手动专门化方法需要一种方法来编写一个可以实现的实现 def foo[@specialized(Int) A: SpecializedFooImpl](a: A): String = implicitly[SpecializedFooImpl[A]](a) ……或者我们可以隐含地保证专业化,如果我们只是 def foo[@specialized(Int) A](a: A)(implicit impl: SpecializedFooImpl[A]): String = impl(a) (实际上,无论如何,这都是较少的样板。) 所以我们已经将提供自定义专业方法的问题转化为需要 手动专业类型 类型类只是类,现在我们必须再次编写专门的类,但是 对于foo,我们需要一个Int版本和一个完全通用的版本。 trait SpecFooImpl[@specialized (Int),A] { def apply(param: A): String } final class SpecFooImplAny[A] extends SpecFooImpl[A] { def apply(param: A) = param.toString } final class SpecFooImplInt extends SpecFooImpl[Int] { def apply(param: Int) = "!" * math.max(0,param) } 现在我们可以创建implicits来提供这样的类型类 implicit def specFooAsAny[A] = new SpecFooImplAny[A] implicit val specFooAsInt = new SpecFooImplInt 除了我们有一个问题:如果我们实际上尝试调用foo:Int,则两个implicits都将适用。 类型类的选择(以及一般的含义) 编译器用于确定隐式使用的权利的秘密成分之一 此外,由于您可以自由定义特征中的含义,因此您可以将它们混合在任何地方。 因此,我们的最后一个难题是将类型类隐含到链中 trait LowPriorityFooSpecializers { implicit def specializeFooAsAny[A] = new SpecializedFooImplAny[A] } trait FooSpecializers extends LowPriorityFooSpecializers { implicit val specializeFooAsInt = new SpecializedFooImplInt } 将最高优先级的特征混合到需要的任何地方,以及 请注意,类型类将与您创建它们一样专门化,即使是 一个完整的例子 假设我们想要制作一个双参数专用的bippy函数 bippy(a,b) -> b bippy(a,b: Int) -> b+1 bippy(a: Int,b) -> b bippy(a: Int,b: Int) -> a+b 我们应该能够通过三种类型和一种专门化来实现这一目标 def bippy[@specialized(Int) A,@specialized(Int) B](a: A,b: B)(implicit impl: SpecBippy[A,B]) = impl(a,b) 然后是类型类: trait SpecBippy[@specialized(Int) A,@specialized(Int) B] { def apply(a: A,b: B): B } final class SpecBippyAny[A,B] extends SpecBippy[A,B] { def apply(a: A,b: B) = b } final class SpecBippyAnyInt[A] extends SpecBippy[A,Int] { def apply(a: A,b: Int) = b + 1 } final class SpecBippyIntInt extends SpecBippy[Int,Int] { def apply(a: Int,b: Int) = a + b } 然后是链式特征的含义: trait LowerPriorityBippySpeccer { // Trick to avoid allocation since generic case is erased anyway! private val mySpecBippyAny = new SpecBippyAny[AnyRef,AnyRef] implicit def specBippyAny[A,B] = mySpecBippyAny.asInstanceOf[SpecBippyAny[A,B]] } trait LowPriorityBippySpeccer extends LowerPriorityBippySpeccer { private val mySpecBippyAnyInt = new SpecBippyAnyInt[AnyRef] implicit def specBippyAnyInt[A] = mySpecBippyAnyInt.asInstanceOf[SpecBippyAnyInt[A]] } // Make this last one an object so we can import the contents object BippySpeccer extends LowPriorityBippySpeccer { implicit val specBippyIntInt = new SpecBippyIntInt } 最后我们将尝试一下(将所有内容粘贴在一起后:粘贴在REPL中): scala> import Speccer._ import Speccer._ scala> bippy(Some(true),"cod") res0: String = cod scala> bippy(1,"salmon") res1: String = salmon scala> bippy(None,3) res2: Int = 4 scala> bippy(4,5) res3: Int = 9 它工作 – 我们的自定义实现已启用。只是为了检查我们是否可以使用 scala> bippy(4,5: Short) res4: Short = 5 scala> bippy(4,5: Double) res5: Double = 5.0 scala> bippy(3: Byte,2) res6: Int = 3 最后,为了验证我们实际上已经避免了拳击,我们将时间生活 scala> val th = new ichi.bench.Thyme th: ichi.bench.Thyme = ichi.bench.Thyme@1130520d scala> val adder = (i: Int,j: Int) => i + j adder: (Int,Int) => Int = <function2> scala> var a = Array.fill(1024)(util.Random.nextInt) a: Array[Int] = Array(-698116967,2090538085,-266092213,... scala> th.pbenchOff(){ var i,s = 0 while (i < 1024) { s = adder(a(i),s); i += 1 } s }{ var i,s = 0 while (i < 1024) { s = bippy(a(i),s); i += 1 } s } Benchmark comparison (in 1.026 s) Not significantly different (p ~= 0.2795) Time ratio: 0.99424 95% CI 0.98375 - 1.00473 (n=30) First 330.7 ns 95% CI 328.2 ns - 333.1 ns Second 328.8 ns 95% CI 326.3 ns - 331.2 ns 所以我们可以看到我们专业的bippy-adder实现了同样的性能 概要 要使用@specialized注释编写自定义专用代码, >使专门的类抽象并手动提供具体的实现 这是很多样板,但最后你可以获得无缝的定制专业体验。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |