Scala中通用Integral的类型不匹配

Pau*_*per 1 generics encoding scala type-conversion

我正在尝试为Scala中的Base64可变长度量量编码解码器.

(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
  }
}
Run Code Online (Sandbox Code Playgroud)

这有一个编译错误:

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

我可以看到这个问题:0,value,和newValue都是类型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;
}
Run Code Online (Sandbox Code Playgroud)

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

Ale*_*nov 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
  }
}
Run Code Online (Sandbox Code Playgroud)

要使用,你必须说出你想要的类型:

编辑:我没有看到你的C++代码n是,T并假设它是一个Int.

评论的答案:

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

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