6

Tôi có một tình huống mà tôi cần một phương pháp mà có thể mất trong các loại loại constructor:Scala loại đệ quy và thực hiện

Array[Int] 
Array[Array[Int]] 
Array[Array[Array[Int]]] 
Array[Array[Array[Array[Int]]]] 
etc... 

chúng ta hãy gọi loại này RAI cho "mảng đệ quy ints"

def make(rai: RAI): ArrayPrinter = { ArrayPrinter(rai) } 

Trường hợp ArrayPrinter là một lớp được khởi tạo với một RAI và lặp qua toàn bộ rai (giả sử nó in tất cả các giá trị trong mảng này [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" 

Nó cũng có thể trả về mảng Array [Array [Int]] ban đầu mà không làm mất bất kỳ thông tin kiểu nào.

val arr: Array[Array[Int]] = printer.getNestedArray() 

Làm cách nào để bạn triển khai tính năng này trong Scala?

+1

Âm thanh như một công việc cho https://index.scala-lang.org/slamdata/matryoshka/matryoshka-core/0.18.3 – Reactormonk

+0

Các sơ đồ một mình đáng để kiểm tra matryoshka! – pedrofurla

Trả lời

3

Trước hết hãy tập trung vào loại. Theo định nghĩa của bạn, một loại T nên typecheck như một cuộc tranh cãi cho ArrayPrinter là nó chấp nhận bởi các chức năng loại sau đây:

def accept[T]: Boolean = 
    T match { // That's everyday business in agda 
    case Array[Int] => true 
    case Array[X] => accept[X] 
    case _   => false 
    } 

Trong Scala, bạn có thể mã hóa mà chức năng loại sử dụng độ phân giải ngầm:

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 

để in những giải pháp đơn giản nhất là bạn phải coi rai: T như mộ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 _   => ??? 
    } 
} 

Bạn cũng có thể được ưa thích và viết print_! sử dụng các loại lớp học, nhưng điều đó có lẽ sẽ kém hiệu quả hơn và mất nhiều thời gian để viết hơn trên ... Còn lại như một bài tập cho người đọc ;-)

0

Cách này là thường được thực hiện bằng cách định nghĩa một lớp trừu tượng có chứa tất cả các chức năng mà bạn muốn liên quan đến kiểu đệ quy này, nhưng không thực sự lấy bất kỳ đối số hàm tạo nào. Thay vào đó, tất cả các phương thức của nó lấy (ít nhất một trong số) kiểu làm đối số. Ví dụ kinh điển sẽ là Ordering. Xác định một hoặc nhiều triển khai ngầm của lớp này, và sau đó bất kỳ lúc nào bạn cần sử dụng nó, chấp nhận nó như một tham số ngầm định. Ví dụ tương ứng sẽ là List's sorted method.

Trong trường hợp của bạn, điều này có thể trông giống như:

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

này có xu hướng được một chút bụi vì phải rằng RAIOps lớp (hoặc ArrayPrinter) mất trong một đối tượng như là một phần của constructor của nó. Điều đó thường dẫn đến nhiều "quyền anh" và "unboxing" hơn, chữ ký kiểu phức tạp, kết hợp mẫu lạ, v.v.

Nó cũng có thêm lợi ích là dễ mở rộng hơn. Nếu sau này người khác có lý do muốn thực hiện ArrayPrinter cho một Set[Int], họ có thể xác định nó cục bộ với mã của họ. Tôi đã nhiều lần xác định một tuỳ chỉnh Ordering.

Các vấn đề liên quan