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.
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)
)))
                There are mainly three types of the array: One Dimensional (1D) Array. Two Dimension (2D) Array. Multidimensional Array.
The total number of elements that can be stored in a multidimensional array can be calculated by multiplying the size of all the dimensions. For example: The array int x[10][20] can store total (10*20) = 200 elements. Similarly array int x[5][10][20] can store total (5*10*20) = 1000 elements.
A 3D array is a multi-dimensional array(array of arrays). A 3D array is a collection of 2D arrays . It is specified by using three subscripts:Block size, row size and column size. More dimensions in an array means more data can be stored in that array.
Multidimensional arrays are an extension of 2-D matrices and use additional subscripts for indexing. A 3-D array, for example, uses three subscripts. The first two are just like a matrix, but the third dimension represents pages or sheets of elements.
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)
))
                        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 } 
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With