91 votes

Comment implémenter des tableaux redimensionnables en Go

Je viens d'un milieu C++ et j'ai l'habitude d'utiliser la fonction std::vector pour ce genre de choses. Supposons que je veuille un tableau dynamique de ces éléments :

type a struct {
    b int
    c string
}

Quelle est la méthode standard pour ce faire ?

Un extrait serait très utile

1 votes

101voto

cthom06 Points 3998

Utiliser le append() intégré

Exemple :

type mytype struct {
  a, b int
}

func main() {
  a := []mytype{mytype{1, 2}, mytype{3, 4}}
  a = append(a, mytype{5, 6})
}

Se référer à la spécimen pour plus d'informations sur append.

0 votes

Une dernière chose cthom06, si vous le voulez bien : J'utilise "for _, t = range(msgs)" où msgs est un vecteur. Je dois ensuite faire passer t dans ma structure msg. Est-ce que je peux faire tout cela à l'intérieur de l'instruction for sur une seule ligne ?

0 votes

@tm1rbrt Je ne crois pas. Du moins, pas en ce qui concerne la portée.

2 votes

Notez que le paquet container/vector a maintenant disparu. Il a été supplanté par les tranches vanille et la fonction append. Les tranches sont essentiellement des tableaux redimensionnables.

75voto

Andrew Gerrand Points 686

Une tranche de Go contient trois éléments : les données, la longueur et la capacité.

s := make([]int, 0, 10)

La variable s est une tranche d'ints d'une longueur de 0 et d'une capacité de 10. Les fonctions intégrées len() et cap() vous permettent d'obtenir la longueur et la capacité d'une tranche :

len(s) == 0
cap(s) == 10

Pour augmenter la longueur d'une tranche, il suffit de la refaire :

s = s[0:5]
// len(s) == 5
// cap(s) == 10

Pour réduire la longueur, vous pouvez prendre une sous-tranche :

s = s[0:1]
// len(s) == 1

Il existe des moyens plus courts d'invoquer make() :

a := make([]int, 10) 
// len(a) == cap(a) == 10

b := make([]int)
// len(b) == cap(b) == 0

C'est très bien, mais que se passe-t-il si vous devez augmenter la longueur d'une tranche au-delà de sa capacité ? Pour ce faire, vous devez allouer une nouvelle tranche et copier le contenu de l'ancienne tranche dans la nouvelle. (La fonction "copy" est une autre fonction intégrée).

t := make([]int, len(s), 20)
copy(t, s)

En Document d'orientation efficace pousse l'exemple un peu plus loin en implémentant une fonction Append qui ajoute une tranche à une autre, en la redimensionnant si nécessaire.

Les tranches sont soutenues par des tableaux ; lorsque vous créez() une tranche d'une capacité spécifique, un tableau de cette capacité est alloué en arrière-plan. La tranche devient alors un "pointeur intelligent" vers ce tableau. Si vous passez cette tranche (ou une sous tranche de cette tranche) à une autre fonction, elle est passée comme un pointeur vers ce même tableau. La création de sous-tranches est donc très bon marché - c'est l'allocation du tableau d'arrière-plan qui est coûteuse.

La bibliothèque standard de Go comprend un certain nombre de paquets de conteneurs - vector, par exemple - qui éliminent la nécessité de gérer manuellement les tranches. Utilisez les slices pour la rapidité, et des classes de conteneurs plus élaborées pour la commodité. (Cela dit, j'utilise toujours les tranches pour la plupart des choses).

Vous vous demandez peut-être pourquoi vous devez vous donner tout ce mal. Après tout, de nombreux langages fournissent des tableaux redimensionnés dynamiquement en tant que primitives. La raison en est liée à la philosophie de Go. Les concepteurs du langage n'ont pas la prétention de savoir quelle est la politique d'allocation appropriée pour votre programme ; au lieu de cela, ils vous donnent les outils dont vous avez besoin pour construire vos propres structures de données.

30voto

Jessta Points 1023

La façon idiomatique de procéder a changé. L'ajout de la fonction append() intégrée signifie que vous pouvez étendre une tranche comme suit :

type a struct {
    b int
    c string
}

func main(){
    var mySlice []a
    mySlice = append(mySlice,a{5,"pizza"})
}

Append() ajoutera l'élément donné à la tranche s'il y a de la place ou étendra la tranche si elle n'est pas assez grande.

Plus d'informations sur append() sont disponibles ici http://golang.org/doc/go_spec.html#Appending_and_copying_slices

4voto

Ashutosh Chamoli Points 163

Pour un Exemple plus simple de la append() intégré

friends := []string{"Adam"}

friends = append(friends, "Rahul") // Add one friend or one string

friends = append(friends, "Angelica", "Rashi") // Add multiple friends or multiple strings

append() Documentation ici

2voto

user414731 Points 11

Vous pouvez également vous contenter d'une tranche, c'est-à-dire d'un tableau qui connaît sa longueur actuelle. Il peut avoir une longueur courante et une capacité maximale distinctes. Notez que les valeurs passées pour la taille et la capacité initiales ne doivent pas nécessairement être des constantes. Vous pouvez donc créer une fonction qui construit et renvoie des tranches de différentes longueurs en fonction de ses paramètres.

L'avantage est qu'une tranche []Int peut être indexée comme un tableau, et renverra des ints lorsqu'elle est utilisée de cette manière.

L'inconvénient est qu'il n'augmentera pas automatiquement au-delà de sa capacité déclarée. Efficacité du Go présente un exemple de la manière dont il faut procéder pour gérer la réaffectation.

le code serait

type mytype struct {
   a, b int
}

func main() {

  sl := make([]mytype, 10, 50) //slice of 10 items, max capacity 50 these do not have to be constant expressions.
  sl[0] = mytype{1,2}
   //...
  for i, value := range sl {
  // ... do stuff with value
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X