Scala: преобразовать несколько функций в одну общую функцию c? - PullRequest
1 голос
/ 17 марта 2020

У меня есть следующее семейство функций (две из которых показаны), которые я хотел бы реализовать как единую универсальную c функцию:

case class RichSeqInt(seq: Seq[Int]) extends AnyVal {
  def toByteArray: Array[Byte] = {
    val buffer = ByteBuffer.allocate(4 * seq.length)
    buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
    seq.foreach(buffer.putInt)
    buffer.array()
  }
}

case class RichSeqLong(seq: Seq[Long]) extends AnyVal {
  def toByteArray: Array[Byte] = {
    val buffer = ByteBuffer.allocate(8 * seq.length)
    buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
    seq.foreach(buffer.putLong)
    buffer.array()
  }
}

Я не могу понять, как в целом получить размер примитивного объекта и затем как вообще поместить его в буфер, даже если это кажется чем-то, что должно быть выполнимо.

Возможно ли это?

Ответы [ 2 ]

3 голосов
/ 17 марта 2020

Вы можете использовать класс типов, но вы потеряете возможность иметь значение типа, потому что этот шаблон требует использования неявного параметра:

import java.nio.{ByteBuffer, ByteOrder}

trait Bufferable[A] {
  def size: Int
  def put(a: A, buffer: ByteBuffer): Unit
}

implicit val intBufferable: Bufferable[Int] =
  new Bufferable[Int] {
    override val size = java.lang.Integer.SIZE / 8
    def put(n: Int, buffer: ByteBuffer): Unit = buffer.putInt(n)
  }

implicit val longBufferable: Bufferable[Long] =
  new Bufferable[Long] {
    override val size = java.lang.Long.SIZE / 8
    def put(n: Long, buffer: ByteBuffer): Unit = buffer.putLong(n)
  }

final case class RichSeq[A](seq: Seq[A])(implicit buf: Bufferable[A]) {
  def toByteArray: Array[Byte] = {
    val buffer = ByteBuffer.allocate(buf.size * seq.length)
    buffer.order(ByteOrder.LITTLE_ENDIAN)
    seq.foreach(buf.put(_, buffer))
    buffer.array()
  }
}

RichSeq(Vector(1, 2, 3)).toByteArray.size // evaluates to 12
RichSeq(Vector(1L, 2L, 3L)).toByteArray.size // evaluates to 24

Вы можете играть с этим кодом здесь, в Scast ie.

Если вы можете, вы, возможно, захотите рассмотреть возможность наличия простого помощника для этого, чтобы вы могли избежать ненужных выделений:

object SeqUtils {
  def toByteArray[A](seq: Seq[A])(implicit buf: Bufferable[A]): Array[Byte] = {
    val buffer = ByteBuffer.allocate(buf.size * seq.length)
    buffer.order(ByteOrder.LITTLE_ENDIAN)
    seq.foreach(buf.put(_, buffer))
    buffer.array()
  }
}

SeqUtils.toByteArray(Vector(1, 2, 3)).size
SeqUtils.toByteArray(Vector(1L, 2L, 3L)).size

Пересмотренный пример далее доступен в Scast ie.

Если вы хотите узнать больше о типовых классах, есть много материалов, доступных в Интернете, я обычно рекомендую это .

0 голосов
/ 17 марта 2020

Это должно сработать, хотя я не уверен, что сделал бы это следующим образом:

  def toByteArray[A: Manifest](seq: Seq[A]): Array[Byte] = seq match {
    case i: Seq[Int] if manifest <:< manifest[Int] => {
      val buffer = ByteBuffer.allocate(4 * seq.length)
      buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
      seq.foreach{x => buffer.putInt(x.asInstanceOf[Int])}
      buffer.array()
    }
    case l: Seq[Long] if manifest <:< manifest[Long] => {
      val buffer = ByteBuffer.allocate(8 * seq.length)
      buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
      seq.foreach{x => buffer.putLong(x.asInstanceOf[Long])}
      buffer.array()    
    }
    case _ => throw new java.lang.UnsupportedOperationException
  }

Конечно, это может быть гораздо более понятный писатель, и некоторые общие части могут быть извлечены. Также напоминание о том, что A <:< B означает, что A должно быть подтипом B. Это выглядит как уловка, но может пригодиться (я узнал в этом году go здесь { ссылка }).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...