2
votes

Aplatir un tableau plat dans Scala

J'ai un tableau plat comme celui-ci et un autre tableau plat qui décrit les dimensions:

case class Elem(elem: Array[Elem])

Alors maintenant, je dois être capable de l'aplatir et de renvoyer un tableau 2 * 2 comme celui-ci:

val unflattened = {{0,1},{2,3}}

Les dimensions peuvent être de n'importe quel ordre. La seule condition est que la longueur du tableau plat soit égale au produit des dimensions. Par exemple, si les dimensions sont

Tableau (3,3)

alors je suppose que le tableau plat elems devra avoir 9 éléments dedans! Les conditions préalables seront vérifiées ailleurs donc je n'ai pas à m'en soucier ici! Tout ce que j'ai à faire est de renvoyer un tableau non aplati.

Comme cela doit fonctionner sur n'importe quelle taille de dimension, je pense que je dois probablement définir une structure récursive pour mettre mes résultats! Quelque chose comme ça?

val elems = Array(0,1,2,3)
val dimensions = Array(2,2)

Cela pourrait-il fonctionner?

Avez-vous une idée de la façon de mettre en œuvre cette fonction?


11 commentaires

Vous devez réfléchir à la manière dont les résultats de cette opération seront utilisés. Cela aidera à clarifier le type de résultat souhaité, ce qui, à son tour, aidera à clarifier l'algorithme que vous devez utiliser pour obtenir ce résultat.


Les résultats seront ultérieurement interprétés par un autre programme. Cette fonction doit simplement prendre ces 2 paramètres et récupérer le résultat comme dimensionnalité appropriée!


Alors, quel est le format des «résultats» qui sont interprétés par l'autre programme? Comment «la dimensionnalité appropriée» est-elle exprimée dans le fichier de données?


Je comprends ce que tu veux dire. Je pourrais renvoyer un objet ou un type Any et c'est à l'appelant d'obtenir le type approprié. Je pourrais également renvoyer des informations telles que le type de retour est un tableau et si oui, combien de dimensions!


Pourriez-vous avoir plus de tableaux à deux dimensions?


@sparkr Quelle sortie attendez-vous si la dimension est Array (2,3)?


Voulez-vous dire un vrai tableau 3D "3 dimensions", un tableau 4D? Similaire à ce que nous avons dans les problèmes de réduction dimensionnelle?


Oui, je pourrais avoir n'importe quelle dimensionnalité!


Donc, pour une dimension de tableau (2,3), j'attends un tableau à 2 dimensions avec 2 lignes et 3 colonnes. Le tableau plat contiendrait alors un total de 6 éléments!


Pour un vrai tableau 3D, votre tableau plat ne peut pas avoir 9 éléments car le nombre doit être un cube et un carré. L'exemple avec 9 éléments donne l'impression d'être un tableau 2D de taille 3x3. S'il ne s'agit que d'un tableau 2D et que votre tableau plat a toujours la taille attendue, alors vous pouvez simplement faire un grouped (n) pour un tableau n * m .


Ok donc pour un tableau 3D, j'aurais les dimensions comme ceci: Array (2,2,2) par exemple!


3 Réponses :


-1
votes

Il existe une fonction groupée sur les tableaux qui fait ce que vous voulez.

@ Array(0,1,2,3).grouped(2).toArray
res2: Array[Array[Int]] = Array(Array(0, 1), Array(2, 3))


1 commentaires

Mais cela ne fonctionnera pas pour un certain nombre de dimensions, n'est-ce pas?



1
votes

Voici une solution:

trait Matrix
case class SimpleMatrix(rows: Vector[Int]) extends Matrix
case class HigherMatrix(matrices: Vector[Matrix]) extends Matrix

def unflatten(flat: Vector[Int], dims: Vector[Int]): Matrix =
  if (dims.length <= 1) {
    SimpleMatrix(flat)
  } else {
    val (Vector(dim), rest) = dims.splitAt(1)

    val subs = flat.grouped(flat.length/dim).map(a => unflatten(a, rest)).toVector

    HigherMatrix(subs)
  }

J'ai utilisé Vector car Array n'est pas vraiment un type Scala et ne le fait pas autorise la conversion de Array [Int] vers Array[Any .

Notez que cela implémente une seule des partitions possibles avec les dimensions données, donc cela peut ou non être ce qui est requis.


Ceci est une version utilisant des types basés sur le trait Matrix dans une autre réponse:

def unflatten(flat: Vector[Any], dims: Vector[Int]): Vector[Any] =
  if (dims.length <= 1) {
    flat
  } else {
    val (Vector(dim), rest) = dims.splitAt(1)

    flat.grouped(flat.length/dim).map(a => unflatten(a, rest)).toVector
  }


0 commentaires

1
votes

Bien que vous devriez pouvoir le faire avec une structure récursive simple, j'ai choisi une structure plus adaptée au problème.

val sm_2__2_2 = arrayToSquareMatrix(Range.inclusive(1, 4).toList, 2, 2)
// sm_2__2_2: Matrix = SimpleMatrix(List(Row(List(1, 2)), Row(List(3, 4))))

val m_2__3_2 = arrayToMatrix(Range.inclusive(1, 6).toList, 2, List(3, 2))
// m_2__3_2: Matrix = SimpleMatrix(List(Row(List(1, 2, 3)), Row(List(4, 5, 6))))

val sm_3__2_2_2 = arrayToSquareMatrix(Range.inclusive(1, 8).toList, 3, 2)
// sm_3__2_2_2: Matrix = HigherMatrix(List(SimpleMatrix(List(Row(List(1, 2)), Row(List(3, 4)))), SimpleMatrix(List(Row(List(5, 6)), Row(List(7, 8))))))

val m_3__3_2_2 = arrayToMatrix(Range.inclusive(1, 12).toList, 3, List(3, 2, 2))
// m_3__3_2_2: Matrix = HigherMatrix(List(SimpleMatrix(List(Row(List(1, 2)), Row(List(3, 4)))), SimpleMatrix(List(Row(List(5, 6)), Row(List(7, 8)))), SimpleMatrix(List(Row(List(9, 10)), Row(List(11, 12))))))

Voici les exemples

case class Row(elems: List[Int])

trait Matrix
case class SimpleMatrix(rows: List[Row]) extends Matrix
case class HigherMatrix(matrices: List[Matrix]) extends Matrix

// since your flat arrays are always of proper sizes... we are not handling error cases
// so we are dealing with higher N-dimension matrices with size List(s1, s2, ...,sN)
// I have chosen List for the example (as its easy to print), you should choose Array

def arrayToMatrix(flat: List[Int], dimension: Int, sizes: List[Int]): Matrix = dimension match {
  case 1 | 2 =>
    // since your flat arrays are always of proper sizes... there should not be any problems here
    SimpleMatrix(
      flat
        .grouped(sizes.head)
        .map(Row)
        .toList
    )
  case _ =>
    HigherMatrix(
      flat
        .grouped(sizes.tail.reduce(_ * _))
        .map(g => arrayToMatrix(g, dimension - 1, sizes.tail))
        .toList
    )
}

def arrayToSquareMatrix(flat: List[Int], dimension: Int, size: Int): Matrix =
  arrayToMatrix(flat, dimension, Range.inclusive(1, dimension).map(_ => size).toList)


13 commentaires

La question n'est pas claire, mais l'idée est de permettre une taille différente dans chaque dimension et non une taille unique dans toutes les dimensions.


@Tim même si cela est nécessaire ... nous pouvons facilement modifier cela pour correspondre à cela.


@Tim Mis à jour pour inclure toutes les tailles dans d'autres dimensions.


Quel est le val sm_3__2? Quelle est sa dimension? Pour moi, c'est une matrice 3x2, ce qui signifie qu'elle devrait avoir 6 éléments au total dans la structure plate, mais votre échantillon a 8 éléments. Cela devrait donc produire une matrice de 3 lignes et 2 colonnes, mais vous avez un résultat de 4 lignes et 2 colonnes. Ce n'est pas correct!


sm_3__2 est une matrice carrée (cube pour être plus précis) 3-dimensionnelle avec des tailles 2 * 2 * 2 , donc elle aura 8 éléments . Et m_3__3_2_2 est une matrice tridimensionnelle non carrée avec des tailles 3 * 2 * 2 donc elle a 12 éléments. Et oui, il y a un problème, le résoudre.


Voici quelque chose de similaire qui a exactement ce que je veux: deeplearning4j.org/docs/latest/nd4j -matrix-manipulation Le premier exemple où une matrice est créée puis transposée est exactement ce dont j'ai besoin!


Voici la méthode de remodelage qui fait exactement ce que je veux: deeplearning4j.org/docs/latest / nd4j-matrice-manipulation


Corrigé. Fonctionne comme prévu maintenant.


Pouvez-vous également corriger / modifier l'exemple sm_3__2? Je n'ai toujours pas tort! Peut-être avez-vous oublié de le modifier!


L'argument dimension semble dupliquer la longueur de la liste tailles .


J'ai ajouté une nouvelle version à ma réponse en utilisant des types inspirés du trait Matrix dans cette réponse, si l'utilisation de Any n'est pas acceptable.


sm_3__2 est correct. c'est une matrice 2 * 2 * 2 signifiant une HigherMatrix contenant 2 SimpleMatrix de 2 * 2.


Changement des noms de variable pour éviter toute confusion.