Tableau bidimensionnel dans Swift

109

Je suis tellement confus à propos des tableaux 2D dans Swift. Laissez-moi vous décrire étape par étape. Et pourriez-vous me corriger si je me trompe.

Tout d'abord; déclaration d'un tableau vide:

class test{
    var my2Darr = Int[][]()
}

Remplissez ensuite le tableau. (comme my2Darr[i][j] = 0où i, j sont des variables de boucle for)

class test {
    var my2Darr = Int[][]()
    init() {
        for(var i:Int=0;i<10;i++) {
            for(var j:Int=0;j<10;j++) {
                my2Darr[i][j]=18   /*  Is this correct?  */
            }
        }
    }
}

Et enfin, modification de l'élément du tableau

class test {
    var my2Darr = Int[][]()
    init() {
        ....  //same as up code
    }
    func edit(number:Int,index:Int){
        my2Darr[index][index] = number
        // Is this correct? and What if index is bigger
        // than i or j... Can we control that like 
        if (my2Darr[i][j] == nil) { ...  }   */
    }
}
Antiokhos
la source
Avez-vous des problèmes avec votre approche?
Alex Wayne
2
Juste pour que vous le sachiez, toute votre deuxième étape peut être réduite à cela var my2DArray = Array(count: 10, repeatedValue: Array(count: 10, repeatedValue: 18))et vous devriez vraiment passer à une version bêta plus récente. Int[][]()n'est plus une syntaxe valide. Il a été changé en [[Int]]().
Mick MacCallum
1
L'init 2D utilisant des valeurs répétées ne fonctionnera pas. Toutes les lignes pointeront vers le même sous-tableau, et ne seront donc pas uniquement accessibles en écriture.
hotpaw2

Réponses:

228

Définir un tableau mutable

// 2 dimensional array of arrays of Ints 
var arr = [[Int]]() 

OU:

// 2 dimensional array of arrays of Ints 
var arr: [[Int]] = [] 

OU si vous avez besoin d'un tableau de taille prédéfinie (comme mentionné par @ 0x7fffffff dans les commentaires):

// 2 dimensional array of arrays of Ints set to 0. Arrays size is 10x5
var arr = Array(count: 3, repeatedValue: Array(count: 2, repeatedValue: 0))

// ...and for Swift 3+:
var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)

Changer d'élément à la position

arr[0][1] = 18

OU

let myVar = 18
arr[0][1] = myVar

Changer le sous-tableau

arr[1] = [123, 456, 789] 

OU

arr[0] += 234

OU

arr[0] += [345, 678]

Si vous aviez un tableau 3x2 de 0 (zéros) avant ces changements, vous avez maintenant:

[
  [0, 0, 234, 345, 678], // 5 elements!
  [123, 456, 789],
  [0, 0]
]

Sachez donc que les sous-tableaux sont mutables et que vous pouvez redéfinir le tableau initial qui représentait la matrice.

Examiner la taille / les limites avant l'accès

let a = 0
let b = 1

if arr.count > a && arr[a].count > b {
    println(arr[a][b])
}

Remarques: mêmes règles de balisage pour les tableaux à 3 et N dimensions.

Keenle
la source
ok une question idiote: comment nous assignons ce tableau, en C nous faisons comme ça: arr [i] [j] = myVar; mais rapidement quand j'essaye de faire de même, j'ai eu cette erreur "'[([(Int)])]. Tapez' n'a pas de membre nommé 'indice'"
Antiokhos
Si vous avez arrdéfini comme dans la réponse, alors myVardevrait être Int, n'est-ce pas?
Keenle
oui c'est int. Et merci beaucoup pour la réponse détaillée .. maintenant c'est clair: D
Antiokhos
6
Dans Swift 3, pour les copains:var arr = Int(repeating: Int(repeating: 0, count: 2), count: 3)
kar
1
Dans Swift 4.2: par exemple, 3 lignes, 2 colonnes, 3 * 2var arr = Array(count: 2, repeatedValue: Array(count: 3, repeatedValue: 0))
Zgpeace
27

À partir de la documentation:

Vous pouvez créer des tableaux multidimensionnels en imbriquant des paires de crochets, où le nom du type de base des éléments est contenu dans la paire la plus interne de crochets. Par exemple, vous pouvez créer un tableau tridimensionnel d'entiers à l'aide de trois ensembles de crochets:

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

Lors de l'accès aux éléments d'un tableau multidimensionnel, l'index d'indice le plus à gauche fait référence à l'élément à cet index dans le tableau le plus à l'extérieur. L'index d'indice suivant à droite fait référence à l'élément à cet index dans le tableau qui est imbriqué à un niveau. Et ainsi de suite. Cela signifie que dans l'exemple ci-dessus, array3D [0] fait référence à [[1, 2], [3, 4]], array3D [0] [1] fait référence à [3, 4] et array3D [0] [1 ] [1] fait référence à la valeur 4.

Woodstock
la source
17

Rendez-le générique Swift 4

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: [T]
    init(rows: Int, columns: Int,defaultValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: defaultValue, count: rows * columns) as! [T]
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}


var matrix:Matrix<Bool> = Matrix(rows: 1000, columns: 1000,defaultValue:false)

matrix[0,10] = true


print(matrix[0,10])
dimo hamdy
la source
J'ai adapté votre réponse pour créer un tableau toroïdal 2D. Merci beaucoup! gist.github.com/amiantos/bb0f313da1ee686f4f69b8b44f3cd184
Brad Root
16

Vous devez être prudent lorsque vous utilisez Array(repeating: Array(repeating: {value}, count: 80), count: 24).

Si la valeur est un objet, qui est initialisé par MyClass(), ils utiliseront la même référence.

Array(repeating: Array(repeating: MyClass(), count: 80), count: 24)ne crée pas une nouvelle instance de MyClassdans chaque élément du tableau. Cette méthode ne crée MyClassqu'une seule fois et la place dans le tableau.

Voici un moyen sûr d'initialiser un tableau multidimensionnel.

private var matrix: [[MyClass]] = MyClass.newMatrix()

private static func newMatrix() -> [[MyClass]] {
    var matrix: [[MyClass]] = []

    for i in 0...23 {
        matrix.append( [] )

        for _ in 0...79 {
            matrix[i].append( MyClass() )
        }
    }

    return matrix
}
Kimi Chiu
la source
Salut, pouvons-nous améliorer cela en tant qu'extension avec le type "anyObject"?
Antiokhos
Bon point sur le problème avec les types de référence. Cependant, pourquoi écrivez-vous Array(repeating: {value}, could 80)avec des accolades {value}? Cela créerait une série de fermetures, n'est-ce pas?
Duncan C
Ou est la {value}méta-notation pour "une valeur de type AnyObject" (un type de référence)?
Duncan C
J'ai passé presque une heure à chercher un bug à cause de ce problème ...
Matheus Weber
13

Dans Swift 4

var arr = Array(repeating: Array(repeating: 0, count: 2), count: 3)
// [[0, 0], [0, 0], [0, 0]]
Garg Ankit
la source
10

Selon les documents Apple pour swift 4.1, vous pouvez utiliser cette structure si facilement pour créer un tableau 2D:

Lien: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html

Exemple de code:

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(repeating: 0.0, count: rows * columns)
    }
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}
Keyhan Kamangar
la source
1
Je l'aime. Cela rappelle l'arithmétique du pointeur C. Ce serait mieux s'il était réécrit à l'aide de Generics, donc cela s'appliquerait aux tableaux à 2 dimensions de n'importe quel type de données. Pour cette question, vous pouvez utiliser cette approche pour créer des tableaux de toute dimension arbitraire.
Duncan C
1
@vacawama, cool, sauf que votre tableau à n dimensions a le même problème que toutes les solutions qui peuplent le tableau en utilisant Array(repeating:count:). Voir le commentaire que j'ai publié sur votre autre réponse.
Duncan C
6

Avant d'utiliser des tableaux multidimensionnels dans Swift, considérez leur impact sur les performances . Dans mes tests, le tableau aplati a fonctionné presque 2x mieux que la version 2D:

var table = [Int](repeating: 0, count: size * size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        let val = array[row] * array[column]
        // assign
        table[row * size + column] = val
    }
}

Temps d'exécution moyen pour remplir une baie 50x50: 82,9 ms

contre.

var table = [[Int]](repeating: [Int](repeating: 0, count: size), count: size)
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        // assign
        table[row][column] = val
    }
}

Temps d'exécution moyen pour remplir une matrice 2D 50x50: 135 ms

Les deux algorithmes sont O (n ^ 2), donc la différence des temps d'exécution est causée par la façon dont nous initialisons la table.

Enfin, le pire que vous puissiez faire est d'utiliser append()pour ajouter de nouveaux éléments. Cela s'est avéré être le plus lent de mes tests:

var table = [Int]()    
let array = [Int](1...size)
for row in 0..<size {
    for column in 0..<size {
        table.append(val)
    }
}

Temps d'exécution moyen pour remplir un tableau 50x50 avec append (): 2,59 s

Conclusion

Évitez les tableaux multidimensionnels et utilisez l'accès par index si la vitesse d'exécution compte. Les tableaux 1D sont plus performants, mais votre code peut être un peu plus difficile à comprendre.

Vous pouvez exécuter les tests de performances vous-même après avoir téléchargé le projet de démonstration depuis mon dépôt GitHub: https://github.com/nyisztor/swift-algorithms/tree/master/big-o-src/Big-O.playground

Karoly Nyisztor
la source
0

Cela peut être fait en une seule ligne.

Swift 5

var my2DArray = (0..<4).map { _ in Array(0..<) }

Vous pouvez également le mapper à des instances de n'importe quelle classe ou structure de votre choix

struct MyStructCouldBeAClass {
    var x: Int
    var y: Int
}

var my2DArray: [[MyStructCouldBeAClass]] = (0..<2).map { x in
    Array(0..<2).map { MyStructCouldBeAClass(x: x, y: $0)}
}
Pimisi
la source