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: (
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). –
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í? –
Đ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