26

次のようにして、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)
    )
    

    この最後の例は、Java でArray言わなければならないよりも 3 倍も必要です。int[][]

これを回避する明確な方法はありますか?

4

6 に答える 6

16

個人的には、わかりやすくするために、「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]] = ...
于 2012-12-14T03:25:13.397 に答える
13

Scala2.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"
              ^

短くなるとは思いません。;)

于 2012-12-13T21:05:03.700 に答える
4

単なるListof の使用List(それ自体では、すべてのサブリストが同じサイズであることを保証できない) が問題ではなく、簡単な構文と作成時のエラーの回避のみに関心がある場合、scala には作成する多くの方法があります。素晴らしい構文構造。

そのような可能性の 1 つは単純なヘルパーです。

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)
)

あなたの懸念について:

「リスト」を何度も繰り返します(リスト以外に何かがあるように)

ここではそうではありません。

すべての List 呼び出しで、末尾の , を省略する必要があります

残念ながら、それはここでも当てはまります。scala の構文規則を考えると、できることはあまりありません。

失敗して配列の途中に List() 以外の何かを挿入すると、コンパイラで問題なく動作しますが、x の型は暗黙のうちに List[List[Int]] ではなく List[Any] になります。

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,

最後に、要素の型を明示的に指定したい場合 (たとえば、Ints とs が誤って混在する可能性を防ぎたい場合)、醜い の代わりにDouble指定するだけです:Matrix[Int]List[List[Int]]

val x = Matrix[Int](
  (3, 5, 7),
  (0, 4, 9),
  (1, 8, 6)
)

編集:あなたの質問で置き換えListたことがわかります。Array配列を使用するには、上記のコードListArraytoListを置き換えるだけです。toArray

于 2012-12-13T15:54:35.277 に答える
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は、空の適用関数に置き換えることをお勧めします。

于 2012-12-14T08:23:52.810 に答える
1

これが簡単な方法かどうかはわかりませんが、ネストされたタプルを「2D」配列に変換するためのコードを以下にいくつか含めました。

まず、タプルのサイズを取得し、タプルを に変換するためのボイラー プレートが必要です[Array[Array[Double]]。私が使用した一連の手順は次のとおりです。

  1. タプルの行数と列数を計算する
  2. ネストされたタプルを 1 行の配列に変換します
  3. 元のタプルのサイズに基づいて配列を再形成します。

そのためのコードは次のとおりです。

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))

または、必要な配列のサイズと値の 1D 配列を使用して直接 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))
于 2012-12-13T16:52:45.107 に答える
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))

クリーン&エレガントに見えますか?
そう思います :)

于 2012-12-16T12:20:38.360 に答える