Scala中通用Integral的类型不匹配

时间:2014-02-05 04:42:43

标签: scala generics encoding type-conversion

我正在尝试为Scala中的Base64 Variable Length Quanties编码解码器。

(Base64 VLQ对有符号整数序列进行编码。编码对编码的整数范围没有限制。)

对于我目前的用例,我知道Seq[Int]就足够了。当然,我想这样做,以便我可以解码为Seq[Long]Seq[BigInteger],甚至可以解码为Seq[Short]

算法完全相同,只是有不同的类型。

仿制药来救援! (或者我想。)


object Base64Vlq {
  val continuationMask = (1 << 5).toByte
  val signMask = 1.toByte

  def decode[T <: Integral[T]](bytes: Seq[Byte]): Seq[T] = {
    val result = scala.collection.mutable.ListBuffer[T]()
    bytes.foldLeft((0, 0)) { (arg, byte) =>
      val (value, n) = arg
      if((byte & continuationMask) == 0) {
        val newValue = value + (byte / 2 & ~continuationMask) << 5 * n
        result += (if((byte & signMask) == 0) { newValue } else { -newValue })
        (0, 0)
      } else {
        (value + (byte & ~continuationMask).toInt << 5 * n, n + 1)
      }
    }
    result
  }
}

这有一个编译错误:

error: type mismatch;
found   : newValue.type (with underlying type Int)
required: T
       result += (if((byte & signMask) == 0) { newValue } else { -newValue })

我可以看到问题:0valuenewValue都是Int类型。

我该怎么做才能解决这个问题?


仅供参考,这就是我在C ++中做同样事情的方法,只需要几行代码。

#include <vector>

template<typename T>
std::vector<T> decode(std::vector<char> bytes) {
    char continuationMask = 1 << 5;
    char signMask = 1;
    vector<T> result;
    int n(0);
    T value(0);
    for(auto it = bytes.begin(); it != bytes.end(); it++) {
        if(*it & continuationMask) {
            value += (*it & ~continuationMask) << 5 * n;
            n++;
        } else {
            value += (*it / 2 & ~continuationMask) << 5 * n;
            result.push_back(*it & signMask ? -value : value);
            value = 0;
            n = 0;
        }
    }
    return result;
}

P.S。作为旁注,如果有人知道公共Maven / Ivy回购中基于Java / Scala的Base64 VLQ实现,即使它不是通用的,我也很高兴知道它。

2 个答案:

答案 0 :(得分:6)

绑定T <: Integral[T]T必须是Integral[T]子类型,这是错误的;你需要一个上下文绑定 T: Integral或者(对于这种情况更好)一个隐式参数(实际上,一个上下文绑定编译成一个隐式参数,唯一的区别是你不能给这是一个名字)。

像这样的东西(未经测试,只是检查它是否编译):

object Base64Vlq {
  val continuationMask = (1 << 5).toByte
  val signMask = 1.toByte

  def decode[T](bytes: Seq[Byte])(implicit ev: Integral[T]): Seq[T] = {
    val result = scala.collection.mutable.ListBuffer[T]()
    val tZero = ev.zero
    bytes.foldLeft((tZero, 0)) { (arg, byte) =>
      val (value, n) = arg
      if((byte & continuationMask) == 0) {
        val newValue = ev.plus(value, ev.fromInt((byte / 2 & ~continuationMask) << 5 * n))
        result += (if((byte & signMask) == 0) { newValue } else { ev.negate(newValue) })
        (tZero, 0)
      } else {
        (ev.plus(value, ev.fromInt((byte & ~continuationMask).toInt << 5 * n)), n + 1)
      }
    }
    result
  }
}

要使用,您必须说明您想要的类型:

编辑:我没有在您的C ++代码n中看到T,并认为它是Int

评论的答案:

  1. 这就像Integral这样的类型有效:它们是Scala的type classes版本。如果要在现有类型上引入新操作,显然无法向它们添加新的超类型,因此会创建Integral[Int]Integral[BigInt]等隐式实例。

  2. 编译器不知道T使用什么类型,您需要告诉它:例如: Base64Vlq.decode[Int](Seq[Byte]())Base64Vlq.decode[BigInt](Seq[Byte]())

答案 1 :(得分:2)

这并不能解决您的问题,但希望它可以帮助您编写通用解决方案。请查看IntegralIntegralOps提供的操作。问题在于位操作。如果选择这种方式,则必须使用Integral和IntegralOps支持的操作重写它们。祝你好运。

object Base64Vlq {
  val continuationMask = (1 << 5).toByte
  val signMask = 1.toByte

  def decode[T](bytes: Seq[Byte])(implicit integral: Integral[T]): Seq[T] = {
    import integral._

    val result = scala.collection.mutable.ListBuffer[T]()
    bytes.foldLeft((integral.zero, integral.zero)) { (arg, byte) =>
      val (value, n) = arg
      if((byte & continuationMask) == integral.zero) {
        // Just a meaningless example calculation:
        result += integral.fromInt(2) + (value * n)

        (integral.zero, integral.zero)
      } else {
        // Just a meaningless example calculation:
        (value + integral.fromInt(3) * n, n + integral.fromInt(1))
      }
    }
    result
  }
}