在 Scala 中初始化一个二维(多维)数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13862568/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Initializing a 2D (multi-dimensional) array in Scala
提问by GreyCat
It's easy to initialize a 2D array (or, in fact, any multidimensional array) in Java by putting something like that:
在 Java 中通过放置类似的东西很容易初始化一个二维数组(或者,实际上,任何多维数组):
int[][] x = new int[][] {
{ 3, 5, 7, },
{ 0, 4, 9, },
{ 1, 8, 6, },
};
It's easy to read, it resembles a 2D matrix, etc, etc.
它易于阅读,类似于二维矩阵等。
But how do I do that in Scala?
但是我如何在 Scala 中做到这一点?
The best I could come up with looks, well, much less concise:
我能想到的最好的外观,嗯,更不简洁:
val x = Array(
Array(3, 5, 7),
Array(0, 4, 9),
Array(1, 8, 6)
)
The problems I see here:
我在这里看到的问题:
- It repeats "Array" over and over again (like there could be anything else besides
Array) - It requires to omit trailing
,in every Array invocation If I screw up and insert something besides
Array()in the middle of array, it will go okay with compiler, but type ofxwould silently becomeArray[Any]instead ofArray[Array[Int]]:val x = Array( Array(3, 5, 7), Array(0, 4), 9, // <= OK with compiler, silently ruins x Array(1, 8, 6) )There is a guard against it, to specify the type directly, but it looks even more overkill than in 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) )This last example needs
Arrayeven 3 times more than I have to sayint[][]in Java.
- 它一遍又一遍地重复“数组”(就像除此之外可能还有其他任何东西
Array) - 它需要
,在每个 Array 调用中省略尾随 如果我搞砸了并
Array()在数组中间插入了一些东西,编译器会没问题,但 type ofx会默默地变成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甚至比我int[][]在 Java 中所说的还要多 3 倍。
Is there any clear way around this?
有什么明确的方法可以解决这个问题吗?
采纳答案by kiritsuku
I suggest to use Scala 2.10 and macros:
我建议使用 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"))
}
}
Usage with:
用法与:
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"
^
I don't think you will get it shorter. ;)
我不认为你会缩短它。;)
回答by Luigi Plinge
Personally I'd suck it up and type out (or cut and paste) "Array" a few times for clarity's sake. Include the type annotation for safety, of course. But if you're really running out of e-ink, a quick easy hack would be simply to provide an alias for Array, for example:
就个人而言,为了清楚起见,我会把它吸起来并输入(或剪切和粘贴)“阵列”几次。当然,为了安全起见,包括类型注释。但是如果你真的用完了电子墨水,一个快速简单的技巧就是为 提供一个别名Array,例如:
val > = Array
val x: Array[Array[Int]] = >(
>(3, 5, 7),
>(0, 4, 9),
>(1, 8, 6)
)
You could also provide a type alias for Arrayif you want to shorten the annotation:
Array如果您想缩短注释,您还可以提供一个类型别名:
type >[T] = Array[T]
val x: >[>[Int]] = ...
回答by Régis Jean-Gilles
If using a mere Listof List(which in itself cannot guarantee that every sub list is of the same size) is not a problem for you, and you are only concerned with easy syntax and avoiding errors at creation-time, scala has many ways to create nice syntax constructs.
如果仅仅使用Listof List(它本身不能保证每个子列表的大小相同)对您来说不是问题,并且您只关心简单的语法并避免创建时的错误,scala 有很多方法可以创建漂亮的语法结构。
One such possibility would be a simple helper:
一个这样的可能性是一个简单的助手:
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)
)
About your concerns:
关于您的顾虑:
It repeats "List" over and over again (like there could be anything else besides List)
它一遍又一遍地重复“列表”(就像除了列表之外可能还有其他任何东西)
Not the case here.
不是这里的情况。
It requires to omit trailing , in every List invocation
它需要在每个 List 调用中省略尾随 ,
Unfortunately that is still true here, not much you can do given scala's syntactic rules.
不幸的是,这里仍然如此,鉴于 Scala 的句法规则,您无能为力。
If I screw up and insert something besides List() in the middle of array, it will go okay with compiler, but type of x would silently become List[Any] instead of List[List[Int]]:
如果我搞砸了并在数组中间插入除了 List() 之外的东西,编译器会没问题,但是 x 的类型会默默地变成 List[Any] 而不是 List[List[Int]]:
val x = List(
List(3, 5, 7),
List(0, 4), 9, // <= OK with compiler, silently ruins x
List(1, 8, 6)
)
The equivalent code now faile to compile:
等效代码现在无法编译:
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,
And finally if you want to explicitly specify the type of elements (say that you want to protect against the possibility of inadvertently mixing Ints and Doubles), you only have to specify Matrix[Int]instead of the ugly List[List[Int]]:
最后,如果你想明确指定元素的类型(比如你想防止无意中混合Ints 和Doubles的可能性),你只需要指定Matrix[Int]而不是丑陋的List[List[Int]]:
val x = Matrix[Int](
(3, 5, 7),
(0, 4, 9),
(1, 8, 6)
)
EDIT: I see that you replaced Listwith Arrayin your question. To use arrays all you have to use is to replace Listwith Arrayand toListwith toArrayin my code above.
编辑:我看到你在你的问题中替换List了Array。要使用数组,您只需要在上面的代码中替换ListwithArray和toListwith 即可toArray。
回答by bluenote10
Since I'm also in disgust with this trailing comma issue (i.e. I cannot simply exchange the last line with any other) I sometimes use either a fluent API or the constructor syntax trick to get the syntax I like. An example using the constructor syntax would be:
由于我也对这个尾随逗号问题感到厌恶(即我不能简单地将最后一行与任何其他行交换),我有时会使用流畅的 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
}
Which allows:
这使得:
// beauty ...
val m = new Matrix {
>(1, 2, 3)
>(4, 5, 6)
>(7, 8, 9)
} build
Unfortunately, this relies on mutable data although it is only used temporarily during the construction. In cases where I want maximal beauty for the construction syntax I would prefer this solution.
不幸的是,这依赖于可变数据,尽管它只是在构建期间临时使用。如果我想要构造语法的最大美感,我会更喜欢这个解决方案。
In case buildis too long/verbose you might want to replace it by an empty apply function.
如果build太长/冗长,您可能希望用空的应用函数替换它。
回答by gilad hoch
glancing through the answers, i did not find what to me seems the most obvious & simple way to do it. instead of Array, you can use a tuple.
would look something like that:
浏览答案,我没有找到在我看来最明显和最简单的方法。代替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))
seems clean & elegant?
i think so :)
看起来干净优雅?
我认同 :)
回答by hohonuuli
I don't know if this is the easy way, but I've included some code below for converting nested tuples into '2D' arrays.
我不知道这是否是简单的方法,但我在下面包含了一些用于将嵌套元组转换为“2D”数组的代码。
First, you need some boiler plate for getting the size of the tuples as well as converting the tuples into [Array[Array[Double]]. The series of steps I used were:
首先,您需要一些样板来获取元组的大小以及将元组转换为[Array[Array[Double]]. 我使用的一系列步骤是:
- Figure out the number of rows and columns in the tuple
- Turn the nested tuple into a one row Array
- Reshape the array based on the size of the original tuple.
- 计算元组中的行数和列数
- 将嵌套的元组变成一行 Array
- 根据原始元组的大小重塑数组。
The code for that is:
代码是:
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)
}
}
Now to use this, you could just do the following:
现在要使用它,您只需执行以下操作:
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))
Alternatively, if you could just call the rowArrayTo2DArray directly using the size of the array you want and a 1D array of values:
或者,如果您可以直接使用所需数组的大小和一维值数组调用 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))

