2013-04-30 40 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