2017-05-20 157 views
6

我有一个情况我需要一个可以在类型的方法:斯卡拉递归类型和类型构造函数实现

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中实现这个功能的?

+1

听起来像一份工作https://index.scala-lang.org/slamdata/matryoshka/matryoshka-core/0.18.3 – Reactormonk

+0

单独的图表是值得检查俄罗斯套娃! – pedrofurla

回答

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: Trai: Any

def print_!: Unit = { 
    def print0(a: Any): Unit = a match { 
    case a: Int  => println(a) 
    case a: Array[_] => a.foreach(print0) 
    case _   => ??? 
    } 
} 

您也可以看中并使用类型类别编写print_!,但这可能会效率更低,需要更多时间来编写比以上内容...留作练习的读者;-)

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)参加对象作为其构造的一部分,清洁了一下。这通常导致更多的“拳击”和“拆箱”,复杂的类型签名,奇怪的模式匹配等。

它还具有更易于扩展的额外好处。如果稍后其他人有理由希望ArrayPrinter实现为Set[Int],则他们可以在本地将其定义为其代码。我有很多次定义了自定义Ordering