代码之家  ›  专栏  ›  技术社区  ›  Yann Moisan cmbaxter

在scala中,如何处理相同参数化类型的异类列表

  •  0
  • Yann Moisan cmbaxter  · 技术社区  · 6 年前

    我有一系列 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)))
      }
    }
    

    根据设计,对于给定的操作,类型在 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])
      }
    }
    

    有更好的方法吗?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Andrey Tyukin    6 年前

    似乎可以通过 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 compute 有点麻烦,但仍然有可能:

    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] => ... } 模式中带有类型参数的部分:这允许我们确保 铸造 返回A t 被接受了 计算 .

        2
  •  1
  •   Alexey Romanov    6 年前

    作为比Andrey Tyukin更通用的解决方案,您可以在外部定义方法 Op ,所以即使 运算 无法修改:

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