【问题标题】:How to avoid type argument erasure如何避免类型参数擦除
【发布时间】:2018-06-18 12:56:08
【问题描述】:

我的代码使用 Scala 和 Breeze 线性代数库。我有DenseVector[Double]DenseVector[Int] 等类型的对象,其中DenseVector 是一个类似数组的容器,具有用于数值计算的专用方法。我有时需要对包含的类型使用模式匹配。类型擦除迫使我引入一个特征和“包装”案例类:

sealed trait DenseVectorRoot
case class DenseVectorReal(val data: DenseVector[Real]) extends DenseVectorRoot
case class DenseVectorMatrixReal(val data: DenseVector[DenseMatrix[Real]]) extends DenseVectorRoot

(其中Real 只是Double 的别名)。

模式匹配如下所示:

def print(data: DenseVectorRoot) =
  data match {
    case DenseVectorMatrixReal(_) => println("Contains real matrices")
    case DenseVectorReal(_) => println("Contains real scalars")
  }

我想摆脱 DenseVectorRoot 特征。我试过这个:

def print2(data: DenseVector[_ <: Any]) =
  data match {
    case _: DenseVector[Double] => println("Contains real matrices")
    case _: DenseVector[Int] => println("Contains real scalars")
  }

但是类型参数被删除了。

我应该如何使用 ClassTags 修改 print2 以便模式匹配起作用?例如,通过在以下代码中打印正确的输出:

val v0 = DenseVector(1.2, 1.5, 1.6)
val v1 = DenseVector(3, 4, 5)

val a = Array(v0, v1)
a.map(print2)

编辑

我需要管理具有不同容器的Array 的主要原因是我的代码需要管理各种类型的数据(例如,解析输入对于DenseVector[Real]DenseVector[Matrix[Real]] 会有所不同) .我目前的设计是将所有内容存储在Array[DenseVectorRoot] 中,然后使用.map() 等高阶函数处理数据。这个函数中的每一个都将在元素到元素的基础上进行模式匹配,以了解数据是DenseVectorReal 还是DenseVectorMatrixReal,并采取相应的行动。

这可能不是解决我的问题的最佳设计,但我在编译时不知道用户提供了哪些类型的数据。我很高兴知道任何更好的设计!

【问题讨论】:

  • 为什么要在DenseVectorRealDenseVectorMatrixReal 上下文中打印出这样的DenseVector(允许知道data 的类型)?
  • 打印只是一个基本的例子。我实际上需要根据类型参数执行不同的数值计算。

标签: scala types pattern-matching


【解决方案1】:

使用TypeTag

您可以要求编译器推断参数类型,并为您生成TypeTag。 然后您可以使用TypeTag 来检查某个类型,或者您可以print 它进行调试。

示例代码

import scala.reflect.runtime.universe._

def printType[A: TypeTag](a: List[A]): Unit = 
  println(if(typeTag[A] == typeTag[Double]) "Double" else "other")

printType(List(1.0))
printType(List(1))

输出

>Double
>other

【讨论】:

    【解决方案2】:

    这类事情最好用类型类来完成:

      trait DenseContent[T] {
        def compute(v: DenseVector[T]): String
      }
      object DenseContent {
        implicit object _Real extends DenseContent[Real] {
          def compute(v: DenseVector[Real]) = "real"
        }
        implicit object _Int extends DenseContent[Int] {
          def compute(v: DenseVector[Int]) = "int"
        }
        // etc ...
      }
    
      def print2[T : DenseContent](data: DenseVector[T]) = println(
         implicitly[DenseContent[T]].compute(data)
      )
    

    【讨论】:

    • 感谢您的回答,这样的架构将使我能够集中更多的行为。新类型也将更容易添加。
    • 我在帖子末尾给出的示例中尝试了它,得到了两个错误:“找不到 p06sandbox.TypeClasses.DenseContent[T] 类型的证据参数的隐含值”和“值计算不是 p06sandbox.TypeClasses.DenseContent[T]"的成员"
    • 好吧,我在答案中引用的 sn-p 编译和工作,如果按字面意思粘贴到 repl 中(我只是在它之前做了 class DenseVector[T]type Real = Double,然后在测试之后 print2(new DenseVector[Int])) .所以,你必须做一些不同的事情。不看代码很难分辨。
    • 也许我错过了一些东西......这是代码:pastebin.com/zB7Ad1e4
    • 你的代码对我来说很好......(我只需要像以前一样定义class DenseVector[T],因为我没有微风,但这不相关)。
    猜你喜欢
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多