Type-safe rectangular multidimensional array type

ⅰ亾dé卋堺 提交于 2019-12-21 12:14:47

问题


How do you represent a rectangular 2-dimensional (or multidimensional) array data structure in Scala?

That is, each row has the same length, verified at compile time, but the dimensions are determined at runtime?

Seq[Seq[A]] has the desired interface, but it permits the user to provide a "ragged" array, which can result in a run-time failure.

Seq[(A, A, A, A, A, A)] (and similar) does verify that the lengths are the same, but it also forces this length to be specified at compile time.

Example interface

Here's an example interface of what I mean (of course, the inner dimension doesn't have to be tuples; it could be specified as lists or some other type):

// Function that takes a rectangular array
def processArray(arr : RectArray2D[Int]) = {
    // do something that assumes all rows of RectArray are the same length
}

// Calling the function (OK)
println(processArray(RectArray2D(
    ( 0,  1,  2,  3),
    (10, 11, 12, 13),
    (20, 21, 22, 23)
)))
// Compile-time error
println(processArray(RectArray2D(
    ( 0,  1,  2,  3),
    (10, 11, 12),
    (20, 21, 22, 23, 24)
)))

回答1:


This is possible using the Shapeless library's sized types:

import shapeless._

def foo[A, N <: Nat](rect: Seq[Sized[Seq[A], N]]) = rect

val a = Seq(Sized(1, 2, 3), Sized(4, 5, 6))
val b = Seq(Sized(1, 2, 3), Sized(4, 5))

Now foo(a) compiles, but foo(b) doesn't.

This allows us to write something very close to your desired interface:

case class RectArray2D[A, N <: Nat](rows: Sized[Seq[A], N]*)

def processArray(arr: RectArray2D[Int, _]) = {
  // Run-time confirmation of what we've verified at compile-time.
  require(arr.rows.map(_.size).distinct.size == 1)
  // Do something.
}

// Compiles and runs.
processArray(RectArray2D(
  Sized( 0,  1,  2,  3),
  Sized(10, 11, 12, 13),
  Sized(20, 21, 22, 23)
))

// Doesn't compile.
processArray(RectArray2D(
  Sized( 0,  1,  2,  3),
  Sized(10, 11, 12),
  Sized(20, 21, 22, 23)
))



回答2:


Using encapsulation to ensure proper size.

final class Matrix[T]( cols: Int, rows: Int ) {
  private val container: Array[Array[T]] = Array.ofDim[T]( cols, rows )
  def get( col: Int, row: Int ) = container(col)(row)
  def set( col: Int, row: Int )( value: T ) { container(col)(row) = value } 
}



回答3:


Note: I misread the question, mistaking a rectangle for a square. Oh, well, if you're looking for squares, this would fit. Otherwise, you should go with @Travis Brown's answer.

This solution may not be the most generic one, but it coincides with the way Tuple classes are defined in Scala.

class Rect[T] private (val data: Seq[T])

object Rect {
    def apply[T](a1: (T, T), a2: (T, T)) = new Rect(Seq(a1, a2))
    def apply[T](a1: (T, T, T), a2: (T, T, T), a3: (T, T, T)) = new Rect(Seq(a1, a2, a3))
    // Continued...
}

Rect(
     (1, 2, 3),
     (3, 4, 5),
     (5, 6, 7))

This is the interface you were looking for and the compiler will stop you if you have invalid-sized rows, columns or type of element.



来源:https://stackoverflow.com/questions/11517437/type-safe-rectangular-multidimensional-array-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!