Scala(或Java)中通用函数的专业化 [英] Specialization of generic functions in Scala (or Java)

查看:40
本文介绍了Scala(或Java)中通用函数的专业化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以在Scala中专门化泛型函数(或类)?例如,我想编写一个将数据写入ByteBuffer的通用函数:

Is it possible to specialize generic functions (or class) in Scala? For example, I want to write a generic function that writes data into a ByteBuffer:

def writeData[T](buffer: ByteBuffer, data: T) = buffer.put(data)

但是由于put方法仅占用一个字节并将其放入缓冲区,因此我需要将其专门用于Ints和Longs,如下所示:

But as the put method takes only a byte and put it into the buffer, I need to specialize it for Ints and Longs as follows:

def writeData[Int](buffer: ByteBuffer, data: Int) = buffer.putInt(data)
def writeData[Long](buffer: ByteBuffer, data: Long) = buffer.putLong(data)

,它将无法编译.当然,我可以改为分别编写3个不同的函数writeByte,writeInt和writeLong,但是让我们说一个数组还有另一个函数:

and it won't compile. Of course, I could instead write 3 different functions writeByte, writeInt and writeLong respectively, but let's say there is another function for an array:

def writeArray[T](buffer: ByteBuffer, array: Array[T]) {
  for (elem <- array) writeData(buffer, elem)
}

,如果没有专门的writeData函数,这将无法工作:我将不得不部署另一组函数writeByteArray,writeIntArray,writeLongArray.每当我需要使用与类型相关的写函数时,必须以这种方式处理这种情况并不酷.我做了一些研究,一种可能的解决方法是测试参数的类型:

and this wouldn't work without the specialized writeData functions: I'll have to deploy another set of functions writeByteArray, writeIntArray, writeLongArray. Having to deal with the situation this way whenever I need to use type-dependent write functions is not cool. I did some research and one possible workaround is to test the type of the parameter:

def writeArray[T](buffer: ByteBuffer, array: Array[T]) {
  if (array.isInstanceOf[Array[Byte]])
    for (elem <- array) writeByte(buffer, elem)
  else if (array.isInstanceOf[Array[Int]])
    for (elem <- array) writeInt(buffer, elem)
  ...
}

这可能有效,但效率较低,因为类型检查是在运行时完成的,与专用功能版本不同.

This might work but it's less efficient because type-checking is done in runtime unlike the specialized function version.

所以我的问题是,用Scala或Java解决这种问题的最理想和最优选的方法是什么?非常感谢您的帮助!

So my question is, what is the most desirable and preferred way to solve this kind of problem in Scala or Java? I appreciate your help in advance!

推荐答案

如果您可以同时拥有一个紧凑而有效的解决方案,那不是很好吗?事实证明,鉴于Scala的 @specialized 功能,您可以.首先警告:该功能有些错误,如果尝试将其用于过于复杂的功能,则可能会中断.但是对于这种情况,它几乎是完美的.

Wouldn't it be nice if you could have both a compact and efficient solution? It turns out that you can, given Scala's @specialized feature. First a warning: the feature is somewhat buggy, and may break if you try to use it for something too complicated. But for this case, it's almost perfect.

@specialized 注释为每种原始类型创建单独的类和/或方法,然后每当编译器确定知道原始类型是什么时,都调用该类和/或方法,而不是泛型版本.唯一的缺点是它会完全自动执行所有这些操作-您无需填写自己的方法.有点可惜,但是您可以使用类型类来克服这个问题.

The @specialized annotation creates separate classes and/or methods for each primitive type, and then calls that instead of the generic version whenever the compiler knows for sure what the primitive type is. The only drawback is that it does all of this completely automatically--you don't get to fill in your own method. That's kind of a shame, but you can overcome the problem using type classes.

让我们看一些代码:

import java.nio.ByteBuffer
trait BufferWriter[@specialized(Byte,Int) A]{
  def write(b: ByteBuffer, a: A): Unit
}
class ByteWriter extends BufferWriter[Byte] {
  def write(b: ByteBuffer, a: Byte) { b.put(a) }
}
class IntWriter extends BufferWriter[Int] {
  def write(b: ByteBuffer, a: Int) { b.putInt(a) }
}
object BufferWriters {
  implicit val byteWriter = new ByteWriter
  implicit val intWriter = new IntWriter
}

这为我们提供了一个通用的 BufferWriter 特性,但是我们覆盖了我们想要的每种特定基本类型(在这种情况下,为 Byte Int )与适当的实现.专业化足够聪明,可以将这种显式版本与通常用于专业化的隐藏版本链接起来.这样您就拥有了自定义代码,但是如何使用它呢?这是隐式val进入的地方(我这样做是为了提高速度和清晰度):

This gives us a BufferWriter trait which is generic, but we override each of the specific primitive types that we want (in this case Byte and Int) with an appropriate implementation. Specialization is smart enough to link up this explicit version with the hidden one it normally uses for specialization. So you've got your custom code, but how do you use it? This is where the implicit vals come in (I've done it this way for speed and clarity):

import BufferWriters._
def write[@specialized(Byte,Int) A: BufferWriter](b: ByteBuffer, ar: Array[A]) {
  val writer = implicitly[BufferWriter[A]]
  var i = 0
  while (i < ar.length) {
    writer.write(b, ar(i))
    i += 1
  }
}

A:BufferWriter 表示意味着要调用此 write 方法,您需要方便地使用一个隐式 BufferWriter [A] .我们已经在 BufferWriters 中为他们提供了val,因此应该进行设置.让我们看看是否可行.

The A: BufferWriter notation means that in order to call this write method, you need to have an implicit BufferWriter[A] handy. We've supplied them with the vals in BufferWriters, so we should be set. Let's see if this works.

val b = ByteBuffer.allocate(6)
write(b, Array[Byte](1,2))
write(b, Array[Int](0x03040506))
scala> b.array
res3: Array[Byte] = Array(1, 2, 3, 4, 5, 6)

如果将这些内容放入文件中,并开始使用 javap -c -private 遍历类,您将看到正在使用适当的原始方法.

If you put these things in a file and start poking around the classes with javap -c -private you'll see that the appropriate primitive methods are being used.

(请注意,如果您不使用专门化,此策略仍然可以使用,但是必须在循环中使用空格将值复制出来.)

(Note that if you didn't use specialization, this strategy would still work, but it would have to box values inside the loop to copy the array out.)

这篇关于Scala(或Java)中通用函数的专业化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆