How do you represent a rectangular 2-dimensional (or multidimensional) array data structure in Scala?
你如何在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 [Seq [A]]具有所需的接口,但它允许用户提供“参差不齐”的数组,这可能导致运行时故障。
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.
Seq [(A,A,A,A,A,A)](和类似的)确实验证长度是否相同,但它也强制在编译时指定此长度。
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)
)))
3 个解决方案
#1
5
This is possible using the Shapeless library's sized types:
这可以使用Shapeless库的大小类型:
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.
现在foo(a)编译,但foo(b)没有编译。
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
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
2
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.
注意:我误读了这个问题,误将矩形误认为是正方形。哦,好吧,如果你正在寻找广场,这将是合适的。否则,你应该选择@Travis Brown的答案。
This solution may not be the most generic one, but it coincides with the way Tuple classes are defined in Scala.
此解决方案可能不是最通用的解决方案,但它与Scala中定义的Tuple类的方式一致。
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.
这是您正在寻找的接口,如果您有无效大小的行,列或元素类型,编译器将阻止您。
#1
5
This is possible using the Shapeless library's sized types:
这可以使用Shapeless库的大小类型:
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.
现在foo(a)编译,但foo(b)没有编译。
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
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
2
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.
注意:我误读了这个问题,误将矩形误认为是正方形。哦,好吧,如果你正在寻找广场,这将是合适的。否则,你应该选择@Travis Brown的答案。
This solution may not be the most generic one, but it coincides with the way Tuple classes are defined in Scala.
此解决方案可能不是最通用的解决方案,但它与Scala中定义的Tuple类的方式一致。
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.
这是您正在寻找的接口,如果您有无效大小的行,列或元素类型,编译器将阻止您。