2013-04-30 21 views
5

我正在寻找良好的做法,以避免一遍又一遍地重写相同的代码来实现拆箱。说我有这样的事情:无锅板Scala ArrayBuilder专业化

def speedyArrayMaker[@specialized(Long) A: ClassTag](...): Array[A] = { 
    val builder = Array.newBuilder[A] 
    // do stuff with builder 
    builder.result 
} 

这将导致拆箱存储底层我builder时可能的,但据我所知,没有拆箱方法,因为我经历的非特ArrayBuilder特点调用它。

在单态世界专门到Long,我会写val builder = new ArrayBuilder.ofLong()和避免装箱可言,但短期告诉ArrayBuilder/Builder将专门对所有原始类型,我想不出一个很好的方式,以避免重复努力在这里。一种方法我已经想到了可能是内speedyArrayMaker

val (add, builder): (A => Unit, ArrayBuilder[A]) = implicitly[ClassTag[A]].runtimeClass match { 
    case java.lang.Long.TYPE => 
    val builder = new ArrayBuilder.ofLong() 
    ((x: Long) => builder += x, builder).asInstanceOf 
    case _ => 
    val builder = Array.newBuilder[A] 
    ((x: A) => builder += x, builder) 
} 

因为它是唯一我们真正关心获取专业的+=方法,然后我们得到了add这是专门在Long一个Function1。与javap的检查,确实是我得到

90: invokestatic #118; //Method scala/runtime/BoxesRunTime.boxToLong:(J)Ljava/lang/Long; 
93: invokeinterface #127, 2; //InterfaceMethod scala/collection/mutable/Builder.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/Builder; 

Array.newBuilder[A]版本(甚至在专业输出)和:

252: invokeinterface #204, 3; //InterfaceMethod scala/Function1.apply$mcVJ$sp:(J)V 

了令人费解的版本。我可以把这个模式放到“专门的构建器帮助器”函数中,但是它会感觉很难看,特别是当它在运行时仍然根据专业化期间编译时已知的事情进行调度时。最终我会说我的建议是搭配Function1已经专业化的事实,我并不特别喜欢它。

是否有巧妙的技巧可以用来使这更愉快?我意识到这是一个非常低级的细节,并且很少会对性能至关重要,但考虑到所有ArrayBuilder.of*专业级别的努力/代码重复,似乎很遗憾地抛弃它们的一些优势来换取是多态的。

编辑 我想到了什么丑,但我希望将工作:

def builderOf(x: Array[Int]): ArrayBuilder.ofInt = new ArrayBuilder.ofInt() 
def builderOf(x: Array[Long]): ArrayBuilder.ofLong = new ArrayBuilder.ofLong() 
def builderOf[A: ClassTag](x: Array[A]): ArrayBuilder[A] = ArrayBuilder.make[A] 

,然后我专门函数内部:

val witness: Array[A] = null 
val builder = builderOf(witness) 

,但它似乎调用通用builderOf即使在专用版本中(即使有足够的类型信息可用于调用Array[Long]版本)。任何人都知道为什么这不起作用?与我提议的另一个相比,这种方法看起来相当干净。我想我希望能有一种更类似于宏观的专业化方法,但我想不能保证它对于所有的实例都是类型正确的,除非它为每个专业化选择相同的方法:(

+0

我不知道看到什么,你会做如何才能让你的任何专业,因为'ArrayBuidler'没有专用的(因此' + ='即使从专门的方法中调用也不会被专门化)。如果完全绕过ArrayBuidler,您将只获得专业化(例如通过定义您自己的专用版本)。 – 2013-04-30 21:01:17

+0

实际上,对我来说,专门研究外部方法(叫'+ ='的方法)可能已经通过允许抖动执行单态缓存内联向我们购买了显着的加速。这是你想到的吗? – 2013-04-30 22:10:40

+0

我的观点(这是我的另一个帐户)是有一大堆专门的'ArrayBuilder'子类,称为'of'','Double'等等。当你询问'Array.newBuilder [someprimitive]'时,会使用它们,但是你也可以直接实例化它们。如果你使用'newBuilder',你会得到一个没有专门化的'ArrayBuilder',但是如果你实例化一个新的ArrayBuilder.ofInt(),你也会得到对'+ ='的无箱调用,这就是我正在尝试的捕捉以上。你可以用更具体和更不具体的类型来注释'new ofInt()'来测试它,看看你是否得到了一个装箱调用。 – copumpkin 2013-04-30 23:53:14

回答

4

你可以尝试沿着线(借口野蛮的名称)的东西,

import scala.collection.mutable.ArrayBuilder 
import scala.reflect.ClassTag 

trait SpecializedArrayBuilder[@specialized(Long) A] { 
    def +=(a: A) 
    def result: Array[A] 
} 

trait LowPrioritySpecializedArrayBuilder { 
    implicit def defaultBuilder[A: ClassTag] = new SpecializedArrayBuilder[A] { 
    val builder = ArrayBuilder.make[A] 
    def +=(a: A) = builder += a 
    def result = builder.result 
    } 
} 

object SpecializedArrayBuilder extends LowPrioritySpecializedArrayBuilder { 
    implicit val longBuilder = new SpecializedArrayBuilder[Long] { 
    val builder = new ArrayBuilder.ofLong 
    def +=(a: Long) = builder += a 
    def result = builder.result 
    } 
} 

object Specialized { 
    def speedyArrayMaker[@specialized(Long) A](a: A) 
    (implicit builder: SpecializedArrayBuilder[A]): Array[A] = { 
    builder += a 
    builder.result 
    } 

    def main(arg: Array[String]) { 
    val arr = speedyArrayMaker(1L) 
    println(arr) 
    } 
} 
+0

感谢您的支持!我还没有机会测试它,但我猜implicits根据专门的类型解决,不像重载的方法,因为我在原始问题中尝试。我认为您编写的特定代码会为我提供整个类型的构建器实例,但不难看出如何修改它以根据需要构建构建器。 – 2013-05-01 14:40:55

+0

从某种意义上说,这只是复制了标准库中已有的大量重复。看起来如果我们想要开始谨慎地将专门化注释添加到集合库的更多部分,“ArrayBuilder”和“Builder”将是一个很好的起点。 – 2013-05-01 14:45:37

+0

@MyseriousDan是的,我同意。 – 2013-05-01 17:07:45