scala - 在Scala中,如何处理相同参数化类型的异构列表
问题描述
我有一系列Any
(在现实生活中,它是 Spark Row
,但足以隔离问题)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
我想对其元素应用一些操作。所以,我定义了一个简单的 ADT 来定义compute
一个类型的操作A
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
鉴于我有一个所有操作的列表,并且我知道要对每个元素应用哪个操作,让我们使用这些操作。
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
cast
按照设计,对于给定的操作,类型在和之间对齐combine
。但不幸的是,以下代码无法编译。错误是
Type mismatch, expected: _$1, actual: AnyVal
有没有办法让它工作?
我通过使用抽象类型成员而不是类型参数找到了一种解决方法。
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
有没有更好的办法 ?
解决方案
似乎您的代码可以通过Op[A]
扩展来简化Any => A
:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
由于它asInstanceOf
无处不在,它不会使代码的安全性低于您以前的安全性。
更新
如果不能改变Op
接口,那么调用cast
andcompute
会比较麻烦,但还是可以的:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
请注意ops(i) match { case op: Opt[t] => ... }
模式中带有类型参数的部分:这允许我们确保cast
返回 at
被compute
.
推荐阅读
- python - python中有没有一种方法可以在一行中有两个输入,而第二个输入不是必需的(如果它不存在则不会崩溃)
- reactjs - 屏蔽 URL - 问题
- python - sqlite3 - 执行许多替代方案来加速从熊猫记录中插入?
- ruby-on-rails - Rails render_to_string with partial 需要表单构建器对象?
- mongodb - 从嵌套文档数组 mongodb 更新特定元素,其中有两个匹配项
- python - 将 DataFrame 中的多个值与另一个不同形状的值进行比较
- python - 如何格式化表示为字符串的数字?
- excel - 如何在 VBA 中复制图表并保留源颜色?
- python - 更改查询参数在 REST GET API 调用中引发 405 错误
- c# - Visual Studio:FOR 循环中的 CS1002