Tri des structures en Go : le chemin court et la magie sous le capot

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Tri des structures en Go : comment écrire court sans se prendre la tête

\n\n

Imaginez : vous avez une slice de structures et vous devez la trier selon n'importe quel champ. Nom, âge, salaire, date de naissance — tout ce que vous voulez. Sur Stack Overflow, la question « What is the shortest way to simply sort an array of structs by (arbitrary) field names? » a recueilli des milliers de vues. Et ce n'est pas un hasard. Go n'a pas de méthode « magique » intégrée comme list.sort(key=lambda x: x.field) en Python. Mais il existe une manière élégante et puissante — sort.Slice. Dans cet article, nous ne nous contentons pas de montrer le code, nous regardons sous le capot : comment fonctionne le tri, ce que fait le runtime et comment éviter les pièges courants.

\n\n

Problème : « Pourquoi ne peut-on pas simplement écrire sort() en Go ? »

\n\n

Si vous venez de Python, JavaScript ou Ruby, vous êtes habitué à une seule ligne :

\n
users.sort(key=lambda u: u.age)
\n

En Go, ce luxe n'existe pas. Le typage strict et l'absence de génériques (avant Go 1.18) obligeaient à écrire une implémentation séparée de sort.Interface pour chaque type de structure. C'était lourd. Par exemple :

\n
type ByAge []User\nfunc (a ByAge) Len() int           { return len(a) }\nfunc (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\nfunc (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }\n\nfunc main() {\n    users := []User{{"Alice", 30}, {"Bob", 25}}\n    sort.Sort(ByAge(users))\n}
\n

Ça fonctionne, mais pour chaque champ — un nouveau type. Et s'il y a 10 champs ? Le code devient une usine à gaz. On veut quelque chose de plus compact.

\n\n

Solution : sort.Slice — votre kit de gentleman

\n\n

Depuis Go 1.8, le package sort contient la fonction Slice. Elle prend une slice et une fonction anonyme (fermeture) qui définit l'ordre. Voici la manière la plus courte :

\n
sort.Slice(users, func(i, j int) bool {\n    return users[i].Age < users[j].Age\n})
\n

Voilà. Trois lignes. Sans déclaration de nouveaux types, sans méthodes superflues. Vous voulez trier par nom ? Changez le champ :

\n
sort.Slice(users, func(i, j int) bool {\n    return users[i].Name < users[j].Name\n})
\n

Et si vous voulez par ordre décroissant ? Changez simplement le signe :

\n
sort.Slice(users, func(i, j int) bool {\n    return users[i].Age > users[j].Age // maintenant par ordre décroissant\n})
\n

Magnifique. Mais comment ça marche ? Et quel est le piège ?

\n\n

Sous le capot : sort.Slice et les fonctions anonymes

\n\n

Analysons ce qui se passe lorsque vous appelez sort.Slice. La fonction est déclarée ainsi :

\n
func Slice(slice interface{}, less func(i, j int) bool)
\n

La première chose qui saute aux yeux est interface{}. La fonction accepte n'importe quelle slice. Comment connaît-elle sa longueur et comment permute-t-elle les éléments ? La réponse — via la réflexion (package reflect).

\n

À l'intérieur, sort.Slice fait ce qui suit :

\n
    \n
  1. Vérifie qu'une slice a été passée. Si vous passez autre chose qu'une slice (par exemple un tableau ou une structure), le programme paniquera à l'exécution.
  2. \n
  3. Extrait la longueur via reflect.ValueOf(slice).Len().
  4. \n
  5. Crée un objet auxiliaire qui implémente dynamiquement l'interface sort.Interface. Cet objet stocke une référence à la slice d'origine (via la réflexion) et à votre fonction less.
  6. \n
  7. Lance l'algorithme de tri standard (quicksort du package sort). Lorsque l'algorithme doit comparer deux éléments, il appelle votre fonction less. Lorsqu'il doit les échanger, il utilise reflect.Swapper(slice).
  8. \n
\n

Point clé : votre fonction less capture la slice d'origine du contexte externe. C'est une fermeture (closure). Le compilateur place la variable users sur le tas pour qu'elle soit accessible même après la sortie de la fonction m

Blogs

Book Recommendations