在Scala中,如何处理同一个参数化类型的异构列表(In Scala, how to deal wi

2019-10-30 04:12发布

我有一个数组Any (在现实生活中,这是一个星火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)))
  }
}

按照设计,对于给定的运算,类型之间对准castcombine 。 但不幸的是下面的代码不会编译。 错误的是

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])
  }
}

有没有更好的办法 ?

Answer 1:

似乎您的代码可以通过使得能够简化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接口,然后调用castcompute较为繁琐了一点,但仍然有可能:

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返回t由接受compute



Answer 2:

由于比安德烈Tyukin的更广泛的解决方案,您可以定义之外的方法Op ,所以它的作品,即使Op不能被修改:

def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

buffer(0) = apply(ops(0), buffer(0))


文章来源: In Scala, how to deal with heterogeneous list of the same parameterized type
标签: scala