2013-04-30 15 views
5

Kutusuzluğa ulaşmak için aynı kodu tekrar tekrar yazmamak için iyi uygulamalar arıyorum. Böyle bir şey olduğunu varsayalım:Boilerplate içermeyen Scala ArrayBuilder uzmanlığı

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

Bu altta yatan kutulanmamış depolama sonuçlanacaktır benim builder mümkün olduğunda ancak anladığım kadarıyla ben unspecialized ArrayBuilder özelliği gidiyorum çünkü hiçbir kutulanmamış yöntem kendisine çağırır. Bir monomorfik dünyada

Long için uzmanlaşmış, ben çoğaltma önlemek için güzel bir yol düşünemiyorum, val builder = new ArrayBuilder.ofLong() yazmak ve tüm boks önlemek, ancak ArrayBuilder/Builder tüm ilkel türlerinde uzmanlaşmış edilecek anlatan kısa ediyorum buradaki çaba. Ben speedyArrayMaker içinde olmak arasında diye düşündüm bir yaklaşım:

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) 
} 

biz gerçekten uzman olsun ister sadece += yöntem, ve sonra biz Long üzerinde uzmanlaşmıştır add için Function1 olsun beri. Gerçekten ben (hatta ihtisas çıktıda) Array.newBuilder[A] versiyonu için

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; 

olsun, javap ile kontrol ediliyor ve: dolambaçlı sürümü için

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

. Bu modeli "özel oluşturucu yardımcı" işlevine dahil edebilirim, ancak özellikle uzmanlık sırasında derleme zamanında bilinen bir şeye göre çalışma zamanında gönderilirken çirkin hissediyor. Nihayetinde benim önerimin, Function1'un zaten uzmanlaşmış olduğu gerçeğini anlatmak olduğunu söyleyebilirim ve özellikle beğenmiyorum.

Bunu daha keyifli hale getirmek için kullanabileceğim akıllı numaralar var mı? Bunun gerçekten düşük seviyeli bir ayrıntı olduğunu ve nadiren performans açısından kritik olduğunu fark ettim, ancak tüm ArrayBuilder.of* uzmanlık sınıflarına giren çaba/kod çoğaltma miktarı göz önünde bulundurulduğunda, avantajlarından bazıları için avantajlar atmak üzücü gibi görünüyor. polimorfik olmak.

Düzenleme çirkin bir şey düşündüm ama umuyordum bu işe yarar : my uzman işlevi içinde

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] 

ve sonra:

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

ama genel builderOf çağırmak gibi görünüyor özel sürümde bile (Array[Long] versiyonunu aramak için yeterli tip bilgisi olsa bile). Bunun neden işe yaradığını bilen var mı? Yaklaşım, önerdiğim diğerine kıyasla oldukça temiz görünüyor. Sanırım uzmanlık için daha "makro gibi" bir yaklaşım umuyordum, ama sanırım her bir uzmanlık için aynı yöntemi seçmedikçe tüm örneklemelerin doğru olacağını garanti edemiyorum :(

+0

sana yapacağım şey 'ArrayBuidler' uzman olmayan' göz önüne alındığında, size bir ihtisas almak (ve dolayısıyla nasıl görmek emin değilim, hatları (barbarca isimleri bahane) boyunca bir şey deneyebilirsiniz + = ', özel bir yöntemle çağrılsa bile asla uzmanlaşmayacaktır).ArrayBuidler'ı tamamen devre dışı bırakırsanız (yalnızca kendi özel sürümünüzü tanımlayarak) sadece uzmanlık kazanacaksınız. –

+0

Aslında, bana, "sadece" dış yöntemi (+ + 'yi çağıran) uzmanlaşmanın, jitterin monorphic önbellek satırlaması yapmasına izin vererek, bizi önemli bir hızlandırma satın alabildiği ortaya çıktı. Aklında olan şey bu mu? –

+0

Benim amacım (bu benim diğer hesabım) '' '' '' '' '' '' '' '' '' 'denilen bir dizi' 'ArrayBuilder' 'alt-sınıfı' 'vardır,' 'Array.newBuilder [someprimitive]' 'için istediğinizde kullanılırlar ama siz onları doğrudan da başlatabilir. Eğer 'newBuilder' kullanıyorsanız, uzman olmayan bir "ArrayBuilder" elde edersiniz, ancak "yeni bir ArrayBuilder.ofInt()" yi başlatırsanız, + + 'ya da denemediğim şeyleri alırsınız. yukarıda yakalamak için. Bunu, 'new ofInt() 'i daha spesifik ve daha az spesifik tiplerle ekleyerek ve bir boks çağrısıyla karşılaşıp karşılaşmadığınızı test edebilirsiniz. – copumpkin

cevap

4

Siz

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

Bunun için teşekkürler! Henüz test etme şansım olmadı, ancak orijinal soruda denediğim gibi, aşırı yüklenmiş yöntemlerin aksine, ihtilafların uzmanlık türüne göre çözüleceğini düşünüyorum. Yazdığınız özel kodun bana tüm tür için bir oluşturucu örneği vereceğini düşünüyorum, ancak talep üzerine yapımcıları değiştirmek için nasıl değiştirileceğini görmek zor değil. –

+0

Bazı durumlarda, bu zaten standart kitaplıkta bulunan çoğaltmanın çoğaltılmasıdır. Dikkatlice koleksiyon kütüphanesinin daha fazla bölümüne özelleştirme ek açıklamaları eklemek istiyorsak, 'ArrayBuilder' ve 'Builder' iyi başlangıç ​​noktaları olacaktır. –

+0

@MyseriousDan Evet, aynı fikirde. –