通过放置类似的内容,可以很容易地用Java初始化2D数组(或者实际上是任何多维数组):
int[][] x = new int[][] {
{ 3, 5, 7, },
{ 0, 4, 9, },
{ 1, 8, 6, },
};
它易于阅读,类似于2D矩阵等,等等。
但是我如何在Scala中做到这一点?
我能想出最好的外观,不那么简洁:
val x = Array(
Array(3, 5, 7),
Array(0, 4, 9),
Array(1, 8, 6)
)
我在这里看到的问题:
Array
之外还有其他任何东西),
如果我搞砸并在数组中间插入除Array()
之外的内容,编译器就可以了,但x
的类型会默默地变为Array[Any]
而不是Array[Array[Int]]
:
val x = Array(
Array(3, 5, 7),
Array(0, 4), 9, // <= OK with compiler, silently ruins x
Array(1, 8, 6)
)
有一个防范它,直接指定类型,但它看起来比在Java中更难过:
val x: Array[Array[Int]] = Array(
Array(3, 5, 7),
Array(0, 4), 9, // <= this one would trigger a compiler error
Array(1, 8, 6)
)
最后一个示例需要Array
甚至比我在Java中说int[][]
多3倍。
有没有明确的方法呢?
答案 0 :(得分:15)
就个人而言,为了清楚起见,我会把它吸干并输入(或剪切和粘贴)“Array”几次。当然,包括安全类型注释。但是,如果你真的没电子墨水了,那么快速简单的黑客就是为Array
提供一个别名,例如:
val > = Array
val x: Array[Array[Int]] = >(
>(3, 5, 7),
>(0, 4, 9),
>(1, 8, 6)
)
如果您想缩短注释,还可以为Array
提供类型别名:
type >[T] = Array[T]
val x: >[>[Int]] = ...
答案 1 :(得分:13)
我建议使用Scala 2.10和宏:
object MatrixMacro {
import language.experimental.macros
import scala.reflect.macros.Context
import scala.util.Try
implicit class MatrixContext(sc: StringContext) {
def matrix(): Array[Array[Int]] = macro matrixImpl
}
def matrixImpl(c: Context)(): c.Expr[Array[Array[Int]]] = {
import c.universe.{ Try => _, _ }
val matrix = Try {
c.prefix.tree match {
case Apply(_, List(Apply(_, List(Literal(Constant(raw: String)))))) =>
def toArrayAST(c: List[TermTree]) =
Apply(Select(Select(Ident("scala"), newTermName("Array")), newTermName("apply")), c)
val matrix = raw split "\n" map (_.trim) filter (_.nonEmpty) map {
_ split "," map (_.trim.toInt)
}
if (matrix.map(_.length).distinct.size != 1)
c.abort(c.enclosingPosition, "rows of matrix do not have the same length")
val matrixAST = matrix map (_ map (i => Literal(Constant(i)))) map (i => toArrayAST(i.toList))
toArrayAST(matrixAST.toList)
}
}
c.Expr(matrix getOrElse c.abort(c.enclosingPosition, "not a matrix of Int"))
}
}
用法:
scala> import MatrixMacro._
import MatrixMacro._
scala> matrix"1"
res86: Array[Array[Int]] = Array(Array(1))
scala> matrix"1,2,3"
res87: Array[Array[Int]] = Array(Array(1, 2, 3))
scala> matrix"""
| 1, 2, 3
| 4, 5, 6
| 7, 8, 9
| """
res88: Array[Array[Int]] = Array(Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9))
scala> matrix"""
| 1, 2
| 1
| """
<console>:57: error: rows of matrix do not have the same length
matrix"""
^
scala> matrix"a"
<console>:57: error: not a matrix of Int
matrix"a"
^
我认为你不会缩短它。 ;)
答案 2 :(得分:4)
如果只使用List
的{{1}}(这本身不能保证每个子列表的大小相同)对您来说不是问题,而您只关心简单的语法和在创建时避免错误,scala有很多方法可以创建漂亮的语法结构。
这种可能性就是一个简单的帮手:
List
关于您的疑虑:
它一遍又一遍地重复“列表”(除了列表之外还有其他任何东西)
不是这里的情况。
它需要在每个List调用中省略尾随
不幸的是,这里仍然存在,考虑到scala的句法规则,你无能为力。
如果我搞砸并在数组中间插入除List()之外的东西,它将适用于编译器,但x的类型将默默地变为List [Any]而不是List [List [Int]]:
object Matrix {
def apply[X]( elements: Tuple3[X, X, X]* ): List[List[X]] = {
elements.toList.map(_.productIterator.toList.asInstanceOf[List[X]] )
}
// Here you might add other overloads for Tuple4, Tuple5 etc if you need "matrixes" of those sizes
}
val x = Matrix(
(3, 5, 7),
(0, 4, 9),
(1, 8, 6)
)
等效代码现在很难编译:
val x = List(
List(3, 5, 7),
List(0, 4), 9, // <= OK with compiler, silently ruins x
List(1, 8, 6)
)
最后,如果你想明确指定元素的类型(假设你想要防止无意中混合scala> val x = Matrix(
| (3, 5, 7),
| (0, 4), 9,
| (1, 8, 6)
| )
<console>:10: error: type mismatch;
found : (Int, Int)
required: (?, ?, ?)
(0, 4), 9,
和Int
),你只需要指定{{ 1}}而不是丑陋的Double
:
Matrix[Int]
编辑:我发现您在问题中用List[List[Int]]
替换了val x = Matrix[Int](
(3, 5, 7),
(0, 4, 9),
(1, 8, 6)
)
。要使用数组,您必须使用的是在上面的代码中将List
替换为Array
,将List
替换为Array
。
答案 3 :(得分:3)
由于我也对这个尾随逗号问题感到厌恶(即我不能简单地将最后一行与任何其他行交换),我有时会使用流畅的API或构造函数语法技巧来获得我喜欢的语法。使用构造函数语法的示例是:
trait Matrix {
// ... and the beast
private val buffer = ArrayBuffer[Array[Int]]()
def >(vals: Int*) = buffer += vals.toArray
def build: Array[Array[Int]] = buffer.toArray
}
允许:
// beauty ...
val m = new Matrix {
>(1, 2, 3)
>(4, 5, 6)
>(7, 8, 9)
} build
不幸的是,这依赖于可变数据,尽管它仅在构造期间暂时使用。如果我想要构造语法的最大美感,我宁愿这个解决方案。
如果build
太长/太冗长,您可能希望用空应用函数替换它。
答案 4 :(得分:1)
我不知道这是否是一种简单的方法,但我在下面列出了一些代码,用于将嵌套元组转换为'2D'数组。
首先,您需要一些样板来获取元组的大小以及将元组转换为[Array[Array[Double]]
。我使用的一系列步骤是:
代码是:
object Matrix {
/**
* Returns the size of a series of nested tuples.
*/
def productSize(t: Product): (Int, Int) = {
val a = t.productArity
val one = t.productElement(0)
if (one.isInstanceOf[Product]) {
val b = one.asInstanceOf[Product].productArity
(a, b)
}
else {
(1, a)
}
}
/**
* Flattens out a nested tuple and returns the contents as an iterator.
*/
def flattenProduct(t: Product): Iterator[Any] = t.productIterator.flatMap {
case p: Product => flattenProduct(p)
case x => Iterator(x)
}
/**
* Convert a nested tuple to a flattened row-oriented array.
* Usage is:
* {{{
* val t = ((1, 2, 3), (4, 5, 6))
* val a = Matrix.toArray(t)
* // a: Array[Double] = Array(1, 2, 3, 4, 5, 6)
* }}}
*
* @param t The tuple to convert to an array
*/
def toArray(t: Product): Array[Double] = flattenProduct(t).map(v =>
v match {
case c: Char => c.toDouble
case b: Byte => b.toDouble
case sh: Short => sh.toDouble
case i: Int => i.toDouble
case l: Long => l.toDouble
case f: Float => f.toDouble
case d: Double => d
case s: String => s.toDouble
case _ => Double.NaN
}
).toArray[Double]
def rowArrayTo2DArray[@specialized(Int, Long, Float, Double) A: Numeric](m: Int, n: Int,
rowArray: Array[A]) = {
require(rowArray.size == m * n)
val numeric = implicitly[Numeric[A]]
val newArray = Array.ofDim[Double](m, n)
for (i <- 0 until m; j <- 0 until n) {
val idx = i * n + j
newArray(i)(j) = numeric.toDouble(rowArray(idx))
}
newArray
}
/**
* Factory method for turning tuples into 2D arrays
*/
def apply(data: Product): Array[Array[Double]] = {
def size = productSize(data)
def array = toArray(data)
rowArrayTo2DArray(size._1, size._2, array)
}
}
现在使用它,您可以执行以下操作:
val a = Matrix((1, 2, 3))
// a: Array[Array[Double]] = Array(Array(1.0, 2.0, 3.0))
val b = Matrix(((1, 2, 3), (4, 5, 6), (7, 8, 9)))
// b: Array[Array[Double]] = Array(Array(1.0, 2.0, 3.0),
// Array(4.0, 5.0, 6.0),
// Array(7.0, 8.0, 9.0))
val c = Matrix((1L, 2F, "3")) // Correctly handles mixed types
// c: Array[Array[Double]] = Array(Array(1.0, 2.0, 3.0))
val d = Matrix((1L, 2F, new java.util.Date())) // Non-numeric types convert to NaN
// d: Array[Array[Double]] = Array(Array(1.0, 2.0, NaN))
或者,如果您可以直接使用所需数组的大小和一维数组值调用rowArrayTo2DArray:
val e = Matrix.rowArrayTo2DArray(1, 3, Array(1, 2, 3))
// e: Array[Array[Double]] = Array(Array(1.0, 2.0, 3.0))
val f = Matrix.rowArrayTo2DArray(3, 1, Array(1, 2, 3))
// f: Array[Array[Double]] = Array(Array(1.0), Array(2.0), Array(3.0))
val g = Matrix.rowArrayTo2DArray(3, 3, Array(1, 2, 3, 4, 5, 6, 7, 8, 9))
// g: Array[Array[Double]] = Array(Array(1.0, 2.0, 3.0),
// Array(4.0, 5.0, 6.0),
// Array(7.0, 8.0, 9.0))
答案 5 :(得分:1)
Array
,你可以使用元组
会是这样的:
scala> val x = {(
| (3,5,7),
| (0,4,9),
| (1,8,6)
| )}
x: ((Int, Int, Int), (Int, Int, Int), (Int, Int, Int)) = ((3,5,7),(0,4,9),(1,8,6))
看起来很干净&amp;优雅?
我是这么认为的:)