2013-04-30 36 views
5

Tôi đang tìm các thực hành tốt để tránh viết lại cùng một mã lặp đi lặp lại để đạt được tính không tự động. Nói rằng tôi có một cái gì đó như thế này:Chuyên gia Scala ArrayBuilder không có Boilerplate miễn phí

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

này sẽ dẫn đến lưu trữ không có hộp bọc cơ bản builder khi có thể, nhưng như tôi hiểu nó, không có phương pháp không có hộp bọc các cuộc gọi đến nó bởi vì tôi đang đi qua không chuyên ArrayBuilder đặc điểm của tôi.

Trong một thế giới monomorphic chuyên để Long, tôi muốn viết val builder = new ArrayBuilder.ofLong() và tránh đấm bốc ở tất cả, nhưng ngắn nói ArrayBuilder/Builder được chuyên về tất cả các loại nguyên thủy, tôi không thể nghĩ ra một cách tốt đẹp để tránh sao chép nỗ lực ở đây. Một cách tiếp cận tôi đã nghĩ đến việc có thể là, trong vòng 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) 
} 

Kể từ khi nó chỉ += phương pháp chúng tôi thực sự quan tâm để có được chuyên môn, và sau đó chúng ta có được một Function1 cho add mà chuyên về Long. Kiểm tra với javap, thực sự tôi nhận được

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; 

cho phiên bản Array.newBuilder[A] (ngay cả trong đầu ra chuyên ngành) và:

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

cho phiên bản phức tạp. Tôi có thể đóng hộp mẫu này thành một hàm "helper builder" chuyên dụng, nhưng nó cảm thấy xấu xí đặc biệt là khi nó vẫn được gửi đi theo thời gian chạy dựa trên một thứ được biết đến trong thời gian biên dịch trong khi chuyên môn hóa. Cuối cùng tôi muốn nói đề nghị của tôi ở đây là để piggyback trên thực tế là Function1 đã được chuyên ngành, và tôi không đặc biệt thích nó.

Có thủ thuật thông minh nào tôi có thể sử dụng để làm cho điều này dễ chịu hơn không? Tôi nhận ra đây là một chi tiết thực sự cấp thấp và hiếm khi có hiệu suất cao, nhưng với số lượng nỗ lực/mã trùng lắp vào tất cả các lớp chuyên biệt ArrayBuilder.of*, có vẻ như một điều đáng tiếc để bỏ đi một số lợi thế của họ để đổi lấy được đa hình.

Sửa Tôi nghĩ đến một cái gì đó xấu xí nhưng mà tôi đã hy vọng sẽ làm việc:

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] 

và sau đó bên trong chức năng chuyên ngành của tôi:

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

nhưng có vẻ như để gọi chung builderOf ngay cả trong phiên bản chuyên biệt (mặc dù thông tin đủ loại có sẵn để gọi phiên bản Array[Long]). Bất cứ ai biết tại sao điều này không hoạt động? Cách tiếp cận này có vẻ khá sạch sẽ, so với cách tiếp cận khác mà tôi đã đề xuất. Tôi đoán tôi đã hy vọng cho một cách tiếp cận "giống như vĩ mô" để chuyên môn hóa, nhưng tôi đoán không có đảm bảo rằng nó sẽ được loại chính xác cho tất cả các instantiations trừ khi nó chọn cùng một phương pháp cho mỗi chuyên môn: (

+0

Tôi không chắc chắn xem bạn sẽ làm gì để có được chuyên môn, vì 'ArrayBuidler' không chuyên biệt (và do đó ' + = 'sẽ không bao giờ được chuyên biệt ngay cả khi được gọi từ bên trong một phương thức chuyên biệt).Bạn sẽ chỉ nhận được chuyên môn nếu bạn bỏ qua 'ArrayBuidler' hoàn toàn (ví dụ bằng cách xác định phiên bản chuyên môn của riêng bạn). –

+0

Trên thực tế, nó xảy ra với tôi rằng chuyên "chỉ" phương pháp bên ngoài (một gọi là '+ =') có thể đã mua cho chúng tôi một tốc độ đáng kể bằng cách cho phép jitter để thực hiện bộ nhớ đệm đơn hình. Đó là những gì bạn có trong tâm trí? –

+0

Điểm của tôi (đây là tài khoản khác của tôi) là có một loạt các lớp con 'ArrayBuilder' chuyên biệt được gọi là' ofInt', 'ofDouble', v.v. Chúng được sử dụng khi bạn yêu cầu' Array.newBuilder [someprimitive] 'nhưng bạn cũng có thể khởi tạo chúng trực tiếp. Nếu bạn sử dụng 'newBuilder', bạn sẽ nhận được một' ArrayBuilder' không chuyên biệt, nhưng nếu bạn khởi tạo một 'new ArrayBuilder.ofInt()', bạn sẽ nhận được các cuộc gọi unboxed tới '+ =' quá, và đó là những gì tôi đã cố gắng để chụp ở trên. Bạn có thể kiểm tra điều này bằng cách chú thích 'new ofInt()' với các kiểu cụ thể hơn và ít cụ thể hơn và xem bạn có nhận được cuộc gọi boxing hay không. – copumpkin

Trả lời

4

Bạn có thể thử điều gì đó dọc theo các dòng (xin lỗi các tên man rợ),

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

Cảm ơn vì điều này! Tôi chưa có cơ hội để kiểm tra nó, nhưng tôi đoán rằng các lời giải thích được giải quyết theo kiểu chuyên biệt, không giống như các phương thức quá tải như tôi đã thử trong câu hỏi ban đầu. Tôi nghĩ rằng mã cụ thể mà bạn đã viết sẽ cung cấp cho tôi một phiên bản trình xây dựng cho toàn bộ loại, nhưng không khó để xem cách sửa đổi nó để làm cho các nhà xây dựng theo yêu cầu. –

+0

Trong một số ý nghĩa, đây chỉ là sao chép rất nhiều bản sao đã có trong thư viện chuẩn. Dường như chúng ta muốn bắt đầu thận trọng thêm các chú thích chuyên môn vào nhiều phần của thư viện bộ sưu tập, 'ArrayBuilder' và' Builder' sẽ là điểm khởi đầu tốt. –

+0

@MyseriousDan Có, tôi đồng ý. –