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实现,即使它不是通用的,我也很高兴知道它.
绑定的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
.
评论的答案:
这就是类型的Integral
工作方式:它们是Scala的类型类型.如果要在现有类型上引入新操作,显然无法向它们添加新的超类型,因此会创建隐式实例Integral[Int]
,Integral[BigInt]
等等.
编译器不知道要使用的类型T
,您需要告诉它:例如Base64Vlq.decode[Int](Seq[Byte]())
或Base64Vlq.decode[BigInt](Seq[Byte]())
.