我遇到的情况是我需要一种可以采用类型的方法:
Array[Int]
Array[Array[Int]]
Array[Array[Array[Int]]]
Array[Array[Array[Array[Int]]]]
etc...
让我们将这种类型的RAI称为“递归的整数数组”
def make(rai: RAI): ArrayPrinter = { ArrayPrinter(rai) }
其中ArrayPrinter是一个用RAI初始化并遍历整个rai的类(假设它打印了这个数组中的所有值[Array [Int]])
val arrayOfArray: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4))
val printer: ArrayPrinter[Array[Array[Int]]] = make(arrayOfArray)
printer.print_! // prints "1, 2, 3, 4"
它还可以返回原始Array [Array [Int]]而不会丢失任何类型信息。
val arr: Array[Array[Int]] = printer.getNestedArray()
如何在Scala中实现此功能?
答案 0 :(得分:3)
让我们首先关注类型。根据您的定义,类型T
应该作为ArrayPrinter
的参数进行类型检查,它是由以下类型函数接受的:
def accept[T]: Boolean =
T match { // That's everyday business in agda
case Array[Int] => true
case Array[X] => accept[X]
case _ => false
}
在Scala中,您可以使用隐式分辨率对该类型函数进行编码:
trait RAI[T]
object RAI {
implicit val e0: RAI[Array[Int]] = null
implicit def e1[T](implicit i: RAI[T]): RAI[Array[T]] = null
}
case class ArrayPrinter[T: RAI](getNestedArray: T) // Only compiles it T is a RAI
要打印内容,最简单的解决方案是将rai: T
视为rai: Any
:
def print_!: Unit = {
def print0(a: Any): Unit = a match {
case a: Int => println(a)
case a: Array[_] => a.foreach(print0)
case _ => ???
}
}
你也可能想要使用类型类来编写print_!
,但这可能效率较低,并且需要花费更多时间来编写以上内容...留给读者的练习; - )
答案 1 :(得分:0)
通常这样做的方法是定义一个抽象类,其中包含您希望与此递归类型相关的所有功能,但实际上并不采用任何构造函数参数。相反,它的所有方法都将(至少一个)类型作为参数。规范示例为Ordering。定义此类的一个或多个隐式实现,然后在需要使用它时,将其接受为隐式参数。相应的示例为List's sorted method。
在您的情况下,这可能如下所示:
abstract class ArrayPrinter[A] {
def mkString(a: A): String
}
implicit object BaseArrayPrinter extends ArrayPrinter[Int] {
override def mkString(x: Int) = x.toString
}
class WrappedArrayPrinter[A](wrapped: ArrayPrinter[A]) extends ArrayPrinter[Array[A]] {
override def mkString(xs: Array[A]) = xs.map(wrapped.mkString).mkString(", ")
}
implicit def makeWrappedAP[A](implicit wrapped: ArrayPrinter[A]): ArrayPrinter[Array[A]] = new WrappedArrayPrinter(wrapped)
def printHello[A](xs: A)(implicit printer: ArrayPrinter[A]): Unit = {
println("hello, array: " + printer.mkString(xs))
}
这比将RAIOps
类(或ArrayPrinter)作为构造函数的一部分接受对象往往更清晰。这通常会导致更多的“拳击”和“拆箱”,复杂的类型签名,奇怪的模式匹配等。
它还具有更容易扩展的额外好处。如果后来其他人有理由想要为Set[Int]
实现ArrayPrinter,他们可以在本地定义它们的代码。我有很多次定义了自定义Ordering
。