Langage Go : tableaux multidimensionnels

Tableaux en Go

Un tableau multidimensionnel est un tableau de tableaux. Considérez-le comme unetable de donnéesoumatrice, où chaque élément peut être accédé via plusieurs indices.

Les tableaux multidimensionnels en Go peuvent servir à gérer des informations structurées telles que des données tabulaires, des opérations matricielles ou des plateaux de jeu.

Concepts de base

  • Tableau unidimensionnel: comme des points sur une ligne droite, une seule coordonnée (indice) est nécessaire pour trouver un élément spécifique
  • Tableau à deux dimensions: comme une table, nécessitant deux coordonnées : ligne et colonne
  • Tableau à trois dimensions: comme un cube, nécessitant trois coordonnées : longueur, largeur et hauteur
  • Dimensions supérieures: peuvent théoriquement avoir de nombreuses dimensions, mais en pratique les tableaux à deux et trois dimensions sont les plus couramment utilisés

Cela peut être comparé à nos places de parking du quotidien :

  • Tableau à une dimension= une rangée de places de parking (il suffit du numéro de place)
  • Tableau à deux dimensions= un parking à plusieurs étages (il faut l'étage et le numéro de place)
  • Tableau à trois dimensions= plusieurs parkings à plusieurs étages (il faut le numéro du parking, l'étage et le numéro de place)

Go prend en charge les tableaux multidimensionnels. Voici les façons courantes de déclarer des tableaux multidimensionnels :

var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type

Déclaration et initialisation :

// Déclarer un tableau à deux dimensions var arrayName [rows][cols]elementType // Déclarer et initialiser var arrayName [rows][cols]elementType = [rows][cols]elementType{initialValues}

L'exemple suivant déclare un tableau d'entiers à trois dimensions :

var threedim [5][10][4]int

Tableau à deux dimensions

Un tableau à deux dimensions est le plus simple des tableaux multidimensionnels. Un tableau à deux dimensions est essentiellement composé de tableaux à une dimension. La façon de définir un tableau à deux dimensions est la suivante :

var arrayName [ x ][ y ] variable_type

variable_type est le type de données en Go, arrayName est le nom du tableau. Un tableau à deux dimensions peut être considéré comme une table, avec x pour les lignes et y pour les colonnes. Le diagramme suivant montre un tableau à deux dimensions a avec trois lignes et quatre colonnes :

Les éléments d'un tableau à deux dimensions sont accessibles viaa[ i ][ j ]pour accéder.

Exemple

package main

import "fmt"

func main() {
    // Étape 1 : Créer un tableau
    values := [][]int{}

    // Étape 2 : Utiliser la fonction append() pour ajouter deux lignes de tableaux à une dimension à un tableau à deux dimensions vide
    row1 := []int{1, 2, 3}
    row2 := []int{4, 5, 6}
    values = append(values, row1)
    values = append(values, row2)

    // Étape 3 : Afficher deux lignes de données
    fmt.Println("Ligne 1")
    fmt.Println(values[0])
    fmt.Println("Ligne 2")
    fmt.Println(values[1])

    // Étape 4 : accéder au premier élément
    fmt.Println("Premier élément :")
    fmt.Println(values[0][0])
}

La sortie de l'exemple ci-dessus est :

Row 1
[1 2 3]
Row 2
[4 5 6]
第一个元素为:
1

Initialisation d'un tableau à deux dimensions

Les tableaux multidimensionnels peuvent être initialisés avec des accolades. L'exemple suivant est un tableau à deux dimensions avec 3 lignes et 4 colonnes :

a := [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引为 0 */
 {4, 5, 6, 7} ,   /*  第二行索引为 1 */
 {8, 9, 10, 11},   /* 第三行索引为 2 */
}
Remarque :Dans le code ci-dessus, l'avant-dernière ligne}doit avoir une virgule, car la dernière ligne}n'a pas besoin d'être sur une seule ligne ; elle peut aussi être écrite comme ceci :
a := [3][4]int{  
 {0, 1, 2, 3} ,   /*  第一行索引为 0 */
 {4, 5, 6, 7} ,   /*  第二行索引为 1 */
 {8, 9, 10, 11}}   /* 第三行索引为 2 */

L'exemple suivant initialise un tableau à deux dimensions avec 2 lignes et 2 colonnes :

Exemple

package main

import "fmt"

func main() {
    // Créer un tableau à deux dimensions
    sites := [2][2]string{}

    // Ajouter des éléments au tableau à deux dimensions
    sites[0][0] = "Google"
    sites[0][1] = "Exemple"
    sites[1][0] = "Taobao"
    sites[1][1] = "Weibo"

    // Afficher le résultat
    fmt.Println(sites)
}

La sortie de l'exemple ci-dessus est :

[[Google Example] [Taobao Weibo]]

Accès à un tableau à deux dimensions

On accède à un tableau à deux dimensions en spécifiant des coordonnées, telles que l'indice de ligne et l'indice de colonne dans le tableau. Par exemple :

val := a[2][3]
ou 
var value int = a[2][3]

L'exemple ci-dessus a accédé au quatrième élément de la troisième ligne du tableau à deux dimensions val.

Un tableau à deux dimensions peut utiliser des boucles imbriquées pour afficher des éléments :

Exemple

package main

import "fmt"

func main() {
   /* Tableau - 5 lignes 2 colonnes*/
   var a = [5][2]int{ {0,0}, {1,2}, {2,4}, {3,6},{4,8}}
   var i, j int

   /* afficher les éléments du tableau */
   for  i = 0; i < 5; i++ {
      for j = 0; j < 2; j++ {
         fmt.Printf("a[%d][%d] = %d\n", i,j, a[i][j] )
      }
   }
}

La sortie de l'exemple ci-dessus est :

a[0][0] = 0
a[0][1] = 0
a[1][0] = 1
a[1][1] = 2
a[2][0] = 2
a[2][1] = 4
a[3][0] = 3
a[3][1] = 6
a[4][0] = 4
a[4][1] = 8

L'exemple suivant crée un tableau multidimensionnel où le nombre d'éléments dans chaque dimension est incohérent :

Exemple

package main

import "fmt"

func main() {
    // Créer un tableau à deux dimensions vide
    animals := [][]string{}

    // Créez trois tableaux à une dimension, chacun avec des longueurs différentes
    row1 := []string{"fish", "shark", "eel"}
    row2 := []string{"bird"}
    row3 := []string{"lizard", "salamander"}

    // Utilisez la fonction append() pour ajouter un tableau à une dimension au tableau à deux dimensions
    animals = append(animals, row1)
    animals = append(animals, row2)
    animals = append(animals, row3)

    // Boucle pour afficher
    for i := range animals {
        fmt.Printf("Row: %v\n", i)
        fmt.Println(animals[i])
    }
}

La sortie de l'exemple ci-dessus est :

Row: 0
[fish shark eel]
Row: 1
[bird]
Row: 2
[lizard salamander]

Accès et modification des éléments d'un tableau

Accéder aux éléments

Exemple

package main

import "fmt"

func main() {
    // Créer une matrice 3x3
    matrix := [3][3]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }
   
    // Accéder à un élément
    fmt.Println("Ligne 1, Colonne 2 :", matrix[0][1])  // Sortie : 2
    fmt.Println("Ligne 3, Colonne 3 :", matrix[2][2]) // Sortie : 9
   
    // Accéder à la ligne entière
    fmt.Println("Ligne 2 :", matrix[1])  // Sortie : [4 5 6]
   
    // Parcourir tous les éléments
    fmt.Println("\nParcourir tous les éléments :")
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j])
        }
    }
}

modifier les éléments

Exemple

package main

import "fmt"

func main() {
    // Créer une matrice nulle 2x2
    var grid [2][2]int
   
    fmt.Println("Matrice avant modification :", grid)
   
    // Modifier l'élément à une position spécifique
    grid[0][0] = 10
    grid[0][1] = 20
    grid[1][0] = 30
    grid[1][1] = 40
   
    fmt.Println("Matrice après modification :", grid)
   
    // Modifier une ligne en lot
    grid[0] = [2]int{100, 200}
    fmt.Println("Après modification de la première ligne :", grid)
}

Tableaux tridimensionnels et de dimensions supérieures

Exemple de tableau tridimensionnel

Exemple

package main

import "fmt"

func main() {
    // Déclarer un tableau tridimensionnel 2x3x4
    // Peut être compris comme : 2 plans, chacun avec 3 lignes et 4 colonnes
    var cube [2][3][4]int
   
    // Initialiser un tableau 3D
    cube = [2][3][4]int{
        { // Le premier plan
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
        },
        { // Le deuxième plan
            {13, 14, 15, 16},
            {17, 18, 19, 20},
            {21, 22, 23, 24},
        },
    }
   
    // Accéder aux éléments d'un tableau 3D
    fmt.Println("cube[0][1][2] =", cube[0][1][2])  // Sortie : 7
    fmt.Println("cube[1][2][3] =", cube[1][2][3])  // Sortie : 24
   
    // Parcourir un tableau 3D
    fmt.Println("\nContenu du tableau tridimensionnel :)
    for i := 0; i < 2; i++ {
        fmt.Printf("Plan %d :\n", i)
        for j := 0; j < 3; j++ {
            for k := 0; k < 4; k++ {
                fmt.Printf("%3d ", cube[i][j][k])
            }
            fmt.Println()
        }
        fmt.Println()
    }
}

voir la figure ci-dessous :


Opérations courantes sur les tableaux multidimensionnels

1. Itération avec range

Exemple

package main

import "fmt"

func main() {
    // Créer un tableau 2D
    scores := [3][4]int{
        {85, 90, 78, 92},
        {88, 76, 95, 89},
        {92, 85, 88, 90},
    }
   
    fmt.Println("Tableau des notes des étudiants :")
   
    // Utiliser range pour parcourir le tableau 2D
    for i, row := range scores {
        fmt.Printf("Notes de l'étudiant %d : ", i+1)
        for j, score := range row {
            fmt.Printf("%d ", score)
            // Si vous avez besoin à la fois de l'index et de la valeur
            _ = j // Éviter l'avertissement de variable inutilisée
        }
        fmt.Println()
    }
   
    // Seule la valeur importe, pas l'index
    total := 0
    count := 0
    for _, row := range scores {
        for _, score := range row {
            total += score
            count++
        }
    }
    fmt.Printf("\nScore moyen : %.2f\n", float64(total)/float64(count))
}

2. Obtenir la longueur du tableau

Exemple

package main

import "fmt"

func main() {
    // Créer un tableau multidimensionnel irrégulier
    jagged := [3][3]int{
        {1, 2, 3},
        {4, 5},
        {6, 7, 8, 9}, // Remarque : Cela provoquera une erreur de compilation car chaque ligne doit avoir la même longueur
    }
   
    // Exemple correct : Obtenir les dimensions du tableau
    matrix := [4][5]int{}
   
    // Obtenir le nombre de lignes
    rows := len(matrix)
    fmt.Println("Lignes :", rows)  // Sortie : 4
   
    // Obtenir le nombre de colonnes dans la première ligne (toutes les lignes ont la même longueur)
    cols := len(matrix[0])
    fmt.Println("Colonnes :", cols)  // Sortie : 5
   
    // Obtenir le nombre total d'éléments
    totalElements := rows * cols
    fmt.Println("Total des éléments :", totalElements)  // Sortie : 20
}

3. Comparaison de tableaux

Exemple

package main

import "fmt"

func main() {
    // Créer deux tableaux 2D identiques
    a := [2][2]int{{1, 2}, {3, 4}}
    b := [2][2]int{{1, 2}, {3, 4}}
    c := [2][2]int{{1, 2}, {3, 5}}
   
    // Les tableaux peuvent être comparés directement (uniquement lorsque les dimensions sont exactement les mêmes)
    fmt.Println("a == b:", a == b)  // Sortie : true
    fmt.Println("a == c:", a == c)  // Sortie : false
   
    // Remarque : des tableaux de dimensions différentes ne peuvent pas être comparés
    // d := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    // fmt.Println(a == d) // Erreur de compilation : types incompatibles
}

Scénarios d'application pratiques

Scénario 1 : plateau de jeu (Tic-Tac-Toe)

Exemple

package main

import "fmt"

func main() {
    // Initialiser un plateau de tic-tac-toe 3x3
    var board [3][3]string
   
    // Initialiser à vide
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            board[i][j] = " "
        }
    }
   
    // Simuler les coups
    board[0][0] = "X"
    board[1][1] = "O"
    board[2][2] = "X"
   
    // Afficher le plateau
    fmt.Println("Plateau de tic-tac-toe :")
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf(" %s ", tableau[i][j])
            if j < 2 {
                fmt.Printf("|")
            }
        }
        fmt.Println()
        if i < 2 {
            fmt.Println("---+---+---")
        }
    }
}

Scénario 2 : Système de gestion des notes des étudiants

Exemple

package main

import "fmt"

func main() {
    // Définir : 3 étudiants, chaque étudiant a 4 cours
    var grades [3][4]float64
   
    // Saisir les notes des étudiants
    grades = [3][4]float64{
        {85.5, 90.0, 78.5, 92.0},  // Notes de l'étudiant 1
        {88.0, 76.5, 95.0, 89.5},  // Notes de l'étudiant 2
        {92.5, 85.0, 88.5, 90.0},  // Notes de l'étudiant 3
    }
   
    // Calculer la moyenne de chaque étudiant
    fmt.Println("Statistiques des notes des étudiants :")
    for i, studentGrades := range grades {
        sum := 0.0
        for _, grade := range studentGrades {
            sum += grade
        }
        average := sum / float64(len(studentGrades))
        fmt.Printf("Étudiant %d : score moyen = %.2f\n", i+1, average)
    }
   
    // Calculer la moyenne de chaque cours
    fmt.Println("\nMoyenne du cours :")
    for j := 0; j < 4; j++ {
        sum := 0.0
        for i := 0; i < 3; i++ {
            sum += grades[i][j]
        }
        average := sum / 3.0
        fmt.Printf("Cours %d : score moyen = %.2f\n", j+1, average)
    }
}

Scénario 3 : Traitement des pixels d'image

Exemple

package main

import "fmt"

func main() {
    // Simuler une simple image en niveaux de gris 3x3
    // Plage de valeurs de chaque pixel : 0(noir) ~ 255(blanc)
    var image [3][3]int
   
    // Initialiser l'image (un dégradé simple)
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            image[i][j] = (i + j) * 50
        }
    }
   
    // Afficher l'image originale
    fmt.Println("Image originale :")
    displayImage(image)
   
    // Traitement d'image : augmenter la luminosité
    fmt.Println("\nImage après augmentation de la luminosité :")
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            // Augmenter la luminosité, mais ne pas dépasser 255
            newValue := image[i][j] + 50
            if newValue > 255 {
                newValue = 255
            }
            image[i][j] = newValue
        }
    }
    displayImage(image)
}

func displayImage(img [3][3]int) {
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("%3d ", img[i][j])
        }
        fmt.Println()
    }
}

Notes et bonnes pratiques

1. La longueur d'un tableau fait partie du type

Exemple

package main

import "fmt"

func main() {
    // Ces deux ont des types différents !
    var a [2][3]int
    var b [3][2]int
   
    // Le code suivant provoquera une erreur de compilation
    // a = b // erreur : incompatibilité de type
   
    fmt.Printf("type de a : %T\n", a)  // [2][3]int
    fmt.Printf("type de b : %T\n", b)  // [3][2]int
}

2. Type valeur vs type référence

Exemple

package main

import "fmt"

func main() {
    // Les tableaux sont des types valeur
    original := [2][2]int{{1, 2}, {3, 4}}
   
    // L'affectation crée une copie
    copy := original
   
    // Modifier la copie n'affecte pas le tableau d'origine
    copy[0][0] = 100
   
    fmt.Println("Tableau d'origine :", original)  // [[1 2] [3 4]]
    fmt.Println("Tableau copié :", copy)      // [[100 2] [3 4]]
   
    // Si vous avez besoin d'une sémantique de référence, vous pouvez utiliser des slices (introduites dans des articles ultérieurs)
}

3. Considérations sur les performances

  • Mémoire contiguë: les tableaux multidimensionnels sont stockés de manière contiguë en mémoire, offrant un accès rapide
  • Taille fixe: la longueur d'un tableau est déterminée à la compilation et ne peut pas être modifiée dynamiquement
  • Scénarios adaptés: lorsque la taille des données est connue et fixe, les tableaux sont le meilleur choix

Questions fréquentes

Q1 : Quelle est la différence entre un tableau multidimensionnel et une slice imbriquée ?

Fonctionnalités Tableaux multidimensionnels Slices imbriquées
Size Fixe, déterminé à la compilation Dynamique, variable à l'exécution
Mémoire Allocation contiguë Éventuellement non contiguë
Performance Vitesse d'accès rapide Légèrement plus lent, présente un surcoût
Cas d'utilisation Taille des données connue Taille des données variable

Q2 : Comment créer un tableau à deux dimensions irrégulier ?

Les tableaux Go exigent que chaque ligne ait la même longueur. Si vous avez besoin d'une structure irrégulière, vous devez utiliser des slices :

Exemple

// Utiliser des slices pour créer des structures irrégulières
irregular := [][]int{
    {1, 2, 3},
    {4, 5},          // Cette ligne n'a que 2 éléments
    {6, 7, 8, 9},    // Cette ligne a 4 éléments
}

Q3 : Un tableau multidimensionnel peut-il être utilisé comme paramètre de fonction ?

Oui, mais notez que les tableaux sont des types valeur, et que le passage de grands tableaux entraîne un coût de performance :

Exemple

func processMatrix(matrix [3][3]int) [3][3]int {
    // Traiter la matrice...
    matrix[0][0] = 100
    return matrix
}

// Une meilleure approche consiste à utiliser des pointeurs ou des slices
func processMatrixPtr(matrix *[3][3]int) {
    matrix[0][0] = 100
}

Tableaux en Go

autres extensions