Dans la section précédente, nous avons discuté des tableaux (arrays) en Go. Cependant, les tableaux ont des limitations : une fois déclarés et initialisés, leur longueur ne peut pas être modifiée. Par conséquent, les tableaux ne sont pas largement utilisés dans la programmation quotidienne. En revanche, les tranches (slices) sont plus couramment utilisées et offrent une structure de données plus flexible.
Points clés de connaissance :
Définir une tranche (slice)
Initialiser une tranche (slice)
Opérations sur les tranches (slices), c'est-à-dire ajouter, supprimer, modifier et rechercher
Étendre une tranche (slice)
Tronquer une tranche (slice)
Tranches (slices) multidimensionnelles
Skills Graph
%%%%{init: {'theme':'neutral'}}%%%%
flowchart RL
go(("Golang")) -.-> go/BasicsGroup(["Basics"])
go(("Golang")) -.-> go/DataTypesandStructuresGroup(["Data Types and Structures"])
go(("Golang")) -.-> go/FunctionsandControlFlowGroup(["Functions and Control Flow"])
go/BasicsGroup -.-> go/values("Values")
go/BasicsGroup -.-> go/variables("Variables")
go/DataTypesandStructuresGroup -.-> go/arrays("Arrays")
go/DataTypesandStructuresGroup -.-> go/slices("Slices")
go/DataTypesandStructuresGroup -.-> go/pointers("Pointers")
go/FunctionsandControlFlowGroup -.-> go/for("For")
subgraph Lab Skills
go/values -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
go/variables -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
go/arrays -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
go/slices -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
go/pointers -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
go/for -.-> lab-149077{{"Structures de données des tranches (slices) en Golang"}}
end
Qu'est-ce qu'une tranche (slice)?
Les tranches (slices) sont similaires aux tableaux (arrays) ; ce sont des conteneurs qui stockent des éléments du même type de données. Cependant, les tableaux ont des limitations : une fois déclarés et initialisés, leur longueur ne peut pas être modifiée. Bien que les tableaux aient leur utilité, ils ne sont pas aussi flexibles. Par conséquent, les tranches sont plus couramment utilisées dans la programmation quotidienne.
En Go, les tranches sont implémentées à l'aide de tableaux. Une tranche est essentiellement un tableau dynamique dont la longueur peut changer. Nous pouvons effectuer des opérations telles que l'ajout, la suppression, la modification et la recherche d'éléments dans une tranche, ce qui n'est pas possible avec un tableau.
Définir une tranche (slice)
La syntaxe d'initialisation d'une tranche (slice) est très similaire à celle d'un tableau (array). La principale différence est que la longueur des éléments n'a pas besoin d'être spécifiée. Regardons le code suivant :
// Declare an array with a length of 5
var a1 [5]byte
// Declare a slice
var s1 []byte
Une tranche (slice) est une référence à un tableau (array)
Si nous déclarons un tableau de type int, la valeur par défaut de chaque élément du tableau sera 0.
Cependant, si nous déclarons une tranche (slice), la valeur par défaut de la tranche sera nil. Créons un fichier appelé slice.go pour vérifier cela :
touch ~/project/slice.go
Entrez le code suivant :
package main
import "fmt"
func main() {
var a [3]int
var s []int
fmt.Println(a[0] == 0) // true
fmt.Println(s == nil) // true
}
go run slice.go
Après avoir exécuté le code, la sortie suivante sera affichée :
true
true
Nous avons créé un tableau a et une tranche s. Nous comparons le premier élément du tableau a avec zéro et vérifions si la tranche s est nil.
Comme nous pouvons le voir, lorsque nous déclarons une tranche, sa valeur par défaut est nil. C'est parce que les tranches ne stockent aucune donnée ; elles ne font que référencer des tableaux. La tranche pointe vers la structure de tableau sous-jacente.
Structure de données d'une tranche (slice)
Une tranche (slice) est un type de données composite, également connu sous le nom de structure (struct). C'est un type composite composé de champs de différents types. La structure interne d'une tranche se compose de trois éléments : un pointeur, une longueur et une capacité.
type slice struct {
elem *type
len int
cap int
}
Comme mentionné précédemment, la structure référence le tableau (array) sous - jacent. Le pointeur elem pointe vers le premier élément du tableau, et le type est le type de l'élément du tableau référencé.
len et cap représentent respectivement la longueur et la capacité de la tranche. Vous pouvez utiliser les fonctions len() et cap() pour obtenir la longueur et la capacité de la tranche.
L'image suivante montre que la tranche référence un tableau sous - jacent de type int, et a une longueur de 8 et une capacité de 10 :
Lorsque vous définissez une nouvelle tranche, le pointeur elem est initialisé à la valeur par défaut (c'est - à - dire nil). Le concept de pointeurs sera introduit dans les laboratoires suivants. Pour l'instant, notez qu'un pointeur pointe vers l'adresse mémoire d'une valeur. Dans l'image ci - dessus, le pointeur elem pointe vers l'adresse du premier élément du tableau sous - jacent.
Opérations sur les tranches (slices) : Ajout, suppression, modification et recherche
Troncature de tableaux (arrays) ou de tranches (slices)
Étant donné que la structure sous - jacente d'une tranche est un tableau, nous pouvons extraire une longueur spécifiée du tableau comme référence pour la tranche. Le segment de code suivant illustre ceci :
package main
import "fmt"
func main() {
// Define an integer array with a length of 10
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// Declare an empty slice
var s1 []int
fmt.Println("Slice s1 is empty:", s1 == nil)
// Use array truncation to obtain the slice
s1 = a[1:5]
s2 := a[2:5]
s3 := a[:]
fmt.Println("Slice s1, s2, and s3 are empty:", s1 == nil, s2 == nil, s3 == nil)
fmt.Println("Elements in array a:", a)
fmt.Println("Elements in slice s1:", s1)
fmt.Println("Elements in slice s2:", s2)
fmt.Println("Elements in slice s3:", s3)
}
La sortie est la suivante :
Slice s1 is empty: true
Slice s1, s2, and s3 are empty: false false false
Elements in array a: [0 1 2 3 4 5 6 7 8 9]
Elements in slice s1: [1 2 3 4]
Elements in slice s2: [2 3 4]
Elements in slice s3: [0 1 2 3 4 5 6 7 8 9]
Dans ce programme, nous déclarons et initialisons d'abord le tableau a, puis nous utilisons la troncature de tableau pour assigner une partie du tableau à la tranche vide s1. Ainsi, nous créons une nouvelle tranche.
s1[1:5] représente la création d'une tranche du tableau. La plage de la tranche va de l'index 1 du tableau a à l'index 5, en excluant le cinquième élément.
Remarque : Dans les langages de programmation, le premier élément a l'index 0, pas 1. De même, le deuxième élément du tableau a l'index 1.
Nous utilisons l'opérateur := pour assigner directement le tableau tronqué à la tranche s2. Il en est de même pour s3, mais aucune plage n'est spécifiée, donc il tronque tous les éléments du tableau.
L'image ci - dessous représente la troncature. Notez que la partie verte de la tranche représente une référence au tableau bleu. En d'autres termes, ils partagent tous deux le même tableau sous - jacent, qui est a.
La syntaxe de troncature pour les tranches est la suivante :
[start:end]
start et end sont tous deux des arguments optionnels. Lorsque nous voulons obtenir tous les éléments du tableau, nous pouvons omettre les arguments start et end. Cela est démontré dans s3 := a[:] du programme précédent.
Si nous voulons récupérer tous les éléments après un certain index, nous pouvons omettre le paramètre end. Par exemple, a1[3:] récupérera tous les éléments à partir de l'index 3.
Pour récupérer tous les éléments avant un certain index, nous pouvons omettre le paramètre start. Par exemple, a1[:4] extraira tous les éléments de l'index 0 à l'index 4, en excluant l'élément à l'index 4.
En plus d'extraire une tranche d'un tableau, nous pouvons également extraire une nouvelle tranche d'une tranche existante. L'opération est la même que pour les tableaux. Voici un exemple simple :
package main
import "fmt"
func main() {
// Define an integer array with a length of 10
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// Create the initial slice s1
var s1 []int
s1 = a[1:7]
fmt.Printf("Slice s1: %d\tLength: %d\tCapacity: %d\n", s1, len(s1), cap(s1))
// Extract a new slice s2 from the initial slice s1
s2 := s1[2:4]
fmt.Printf("Slice s1: %d\tLength: %d\tCapacity: %d\n", s2, len(s2), cap(s2))
}
Dans ce programme, nous avons obtenu la tranche s1 en tronquant le tableau a. La plage de s1 va de l'index 1 à l'index 7. En utilisant :=, nous avons extrait une nouvelle tranche s2 de s1. Étant donné que la troncature de s1 est contiguë, la nouvelle tranche s2 sera également contiguë.
Nous remarquons que la capacité d'une tranche change lorsque nous la tronquons. Les règles sont les suivantes :
Si nous tronquons une tranche d'une capacité de c, la longueur de s[i:j] sera j - i, et la capacité sera c - i.
Pour s1, le tableau sous - jacent est a, et la capacité de a[1:7] est 9 (c'est - à - dire 10 - 1).
Pour s2, le tableau sous - jacent est le même que celui de s1. Étant donné que la partie tronquée a une capacité de 9 à l'étape précédente, la capacité de s1[2:4] devient 7 (c'est - à - dire 9 - 2).
Les modifications des valeurs d'une tranche affectent simultanément les valeurs des éléments du tableau sous - jacent
Étant donné que la tranche ne stocke pas de données, mais ne fait que référencer un tableau, modifier la valeur d'une tranche changera également simultanément la valeur du tableau sous - jacent. Illustrons cela :
package main
import "fmt"
func main() {
// Define an integer array with a length of 10
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := a[2:5]
s2 := a[:]
fmt.Println("Before Modification: ")
fmt.Println("Elements in array a: ", a)
fmt.Println("Elements in array a: ", s1)
fmt.Println("Elements in array a: ", s2)
// Modify the value at index 2 of the slice s1 to 23
s1[2] = 23
fmt.Println("After Modification: ")
fmt.Println("Elements in array a: ", a)
fmt.Println("Elements in array a: ", s1)
fmt.Println("Elements in array a: ", s2)
}
La sortie est la suivante :
Before Modification:
Elements in array a: [0 1 2 3 4 5 6 7 8 9]
Elements in array a: [2 3 4]
Elements in array a: [0 1 2 3 4 5 6 7 8 9]
After Modification:
Elements in array a: [0 1 23 3 4 5 6 7 8 9]
Elements in array a: [2 3 23]
Elements in array a: [0 1 23 3 4 5 6 7 8 9]
Dans ce programme, les tranches s1 et s2 référencent toutes deux le tableau a. Lorsque la tranche s1 modifie la valeur à l'index 2 en 23, les valeurs du tableau a et de la tranche s2 sont également mises à jour.
Nous pouvons voir que la valeur à l'index 2 du tableau est modifiée en 23, ce qui entraîne la modification de la valeur à l'index 4 de la tranche s2.
Cela pourrait entraîner des bugs difficiles à déboguer dans le développement de programmes. Par conséquent, dans la programmation quotidienne, nous devrions essayer d'éviter au maximum que plusieurs tranches référencent le même tableau sous - jacent.
Ajout d'éléments à une tranche
Dans cette section, nous allons présenter la fonction append, qui est utilisée pour ajouter des éléments à une tranche. La syntaxe est la suivante :
func append(slice []Type, elems...Type) []Type
Le premier argument est la tranche slice, et les arguments restants sont les éléments à ajouter à la tranche. Le []Type à la fin indique que la fonction append renverra une nouvelle tranche du même type de données que slice.
Le elems après ... indique qu'il s'agit d'un paramètre variable, ce qui signifie qu'un ou plusieurs paramètres peuvent être entrés.
Voici un exemple d'utilisation de la fonction append :
Dans ce programme, nous créons d'abord la tranche s1 en tronquant le tableau a. La plage de s1 va de l'index 1 à l'index 7. Nous utilisons l'opérateur := pour assigner la valeur de retour de append à s1. Lorsque nous ajoutons un élément en utilisant append, la capacité de la tranche peut changer. La capacité de s1 sera doublée si le nombre d'éléments dépasse la capacité.
Les règles d'expansion des tranches sont les suivantes :
Si le tableau sous - jacent d'une tranche peut accueillir de nouveaux éléments, la capacité de la tranche ne changera pas.
Si le tableau sous - jacent d'une tranche ne peut pas accueillir de nouveaux éléments, Go créera un tableau plus grand, copiera les valeurs de la tranche originale dans le nouveau tableau, puis ajoutera la valeur ajoutée au nouveau tableau.
Remarque : L'expansion des tranches n'est pas toujours une multiplication par deux ; cela dépend de la taille des éléments, du nombre d'éléments à ajouter et du matériel de l'ordinateur, entre autres facteurs. Pour plus d'informations, veuillez consulter la section avancée sur les tranches.
Suppression d'éléments dans une tranche
Go ne fournit pas de mots - clés ou de fonctions pour supprimer des éléments d'une tranche, mais nous pouvons utiliser la troncature de tableau pour obtenir la même fonctionnalité, voire une fonctionnalité plus puissante.
Le code suivant supprime l'élément à l'index 5 de la tranche s et l'assigne à une nouvelle tranche s1 :
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// Print the slice `s` before deleting
fmt.Println(s)
s1 := append(s[:5], s[6:]...)
// Print the slices `s` and `s1` after deleting the element at index 5
fmt.Printf("%d\n%d\n", s, s1)
}
La clé pour supprimer un index spécifique réside dans l'instruction append. Elle ajoute les éléments après l'index 6 aux éléments avant l'index 5.
Cette opération est équivalente à une écrasement préalable. Par exemple, l'élément 5 est remplacé par l'élément 6, l'élément 6 est remplacé par l'élément 7, et ainsi de suite, jusqu'à l'élément 9, qui est ajouté à la nouvelle tranche. Par conséquent, la valeur du tableau sous - jacent à ce moment est [0 1 2 3 4 6 7 8 9 9].
À ce stade, si nous vérifions la longueur et la capacité de s1, nous pouvons voir que la longueur est 9, mais la capacité est 10. C'est parce que s1 est une référence à la troncature de s. Ils partagent le même tableau sous - jacent.
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(s)
s1 := append(s[:5], s[6:]...)
// Check the length and capacity of s1
fmt.Println(len(s1), cap(s1))
fmt.Printf("\n%d\n%d\n\n", s, s1)
// Modify the slice s
s[3] = 22
fmt.Printf("%d\n%d\n", s, s1)
}
En plus de supprimer des éléments à des positions spécifiques, nous pouvons également supprimer une plage spécifique d'éléments d'une tranche en utilisant la troncature. L'opération est la même que pour supprimer un élément à un index spécifique. Faisons un petit exercice.
Créez un fichier slice1.go. Créez une tranche a et initialisez - la comme suit. Ensuite, utilisez la troncature pour créer une autre tranche s qui n'inclut pas les éléments supérieurs à 3 ou inférieurs à 7. Enfin, affichez la nouvelle tranche.
a := []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
Sortie :
[9 8 7 3 2 1 0]
Indice : Faites attention à l'index de départ de la tranche (n'oubliez pas que l'index commence à 0).
Exigences : Le fichier slice1.go doit être placé dans le répertoire ~/project.
Une tranche (slice) a deux attributs : len et cap. len représente le nombre d'éléments actuellement présents dans la tranche, tandis que cap représente le nombre maximal d'éléments que la tranche peut contenir.
Que se passe - t - il lorsque le nombre d'éléments ajoutés à une tranche dépasse sa capacité? Découvrons - le ensemble :
Pour exécuter le programme, exécutez la commande suivante :
go run slice.go
La sortie est la suivante :
Before Append in s1: [0 0 0] Length: 3 Capacity: 3
Before Append in s2: [0 0 0] Length: 3 Capacity: 5
After Append in s1: [0 0 0 12] Length: 4 Capacity: 6
After Append in s2: [0 0 0 22] Length: 4 Capacity: 5
Comme nous pouvons le voir dans ce programme, lorsque nous ajoutons des éléments à une tranche et que le nombre d'éléments dépasse sa capacité originale, la capacité de la tranche est automatiquement augmentée.
Les règles d'expansion d'une tranche sont les suivantes :
Si le tableau (array) sous - jacent d'une tranche peut accueillir de nouveaux éléments, la capacité de la tranche ne changera pas.
Si le tableau sous - jacent d'une tranche ne peut pas accueillir de nouveaux éléments, Go créera un tableau plus grand, copiera les valeurs de la tranche originale dans le nouveau tableau, puis ajoutera les valeurs ajoutées au nouveau tableau.
Remarque : Le doublement de la capacité d'une tranche n'est pas toujours le cas ; cela dépend de la taille des éléments, du nombre d'éléments et du matériel de l'ordinateur. Pour plus d'informations, veuillez consulter la section avancée sur les tranches.
Copie de tranches (slices)
Nous pouvons utiliser la fonction copy pour dupliquer une tranche dans une autre. La syntaxe est la suivante :
func copy(dst, src []Type) int
dst est la tranche de destination, src est la tranche source, et le int final indique le nombre d'éléments copiés, qui est le minimum entre len(dst) et len(src).
Remarque : La fonction copy n'ajoute pas d'éléments.
Pour exécuter le programme, exécutez la commande suivante :
go run slice.go
La sortie est la suivante :
2 [0 1 2 3] [0 1]
2 [8 9 2 3] [8 9]
Dans ce programme, nous avons copié les valeurs de la tranche s1 dans la tranche s2, et les valeurs de la tranche s4 dans la tranche s3. La fonction copy renvoie le nombre d'éléments copiés.
Nous notons que les valeurs de s1 et s2 sont les mêmes, de même que les valeurs de s3 et s4. La première fonction copy copie s1[0, 1, 2, 3] dans s2[8, 9]. Étant donné que la longueur minimale de s1 et s2 est 2, elle copie 2 valeurs. La tranche de destination s2 est modifiée en [0, 1].
La deuxième fonction copy copie s4[8, 9] dans s3[0, 1, 2, 3]. Étant donné que la longueur minimale de s3 et s4 est 2, elle copie 2 valeurs. En conséquence, s3 est modifié en [8, 9, 2, 3].
Parcours de tranches (slices)
Le parcours d'une tranche est similaire au parcours d'un tableau (array). Toutes les méthodes de parcours de tableau peuvent également être utilisées pour les tranches.
Exercice
Dans cet exercice, nous allons tester et renforcer notre compréhension du parcours de tranches et de tableaux.
Créez un fichier slice2.go. Déclarez un tableau a1 et une tranche s1, et initialisez - les comme suit. Ensuite, parcourez les éléments du tableau a1 et de la tranche s1, et affichez leurs indices et valeurs.
Element a1 at index 0 is 1
Element a1 at index 1 is 2
Element a1 at index 2 is 3
Element a1 at index 3 is 9
Element a1 at index 4 is 7
Element s1 at index 0 is 1
Element s1 at index 1 is 8
Element s1 at index 2 is 12
Element s1 at index 3 is 1
Element s1 at index 4 is 3
Exigences : Le fichier slice2.go doit être placé dans le répertoire ~/project.
Indice : Vous pouvez utiliser le format 'range' ou le format d'index pour parcourir les éléments.
Dans cette section, nous avons appris à connaître les tranches (slices) et leur utilisation. Comparativement, les tranches sont plus flexibles et polyvalentes que les tableaux (arrays). Lorsque nous travaillons avec plusieurs tranches, nous devons être prudents pour éviter des opérations inattendues sur les tranches.