mercredi 13 septembre 2023

De python à Golang: les tris

 Python propose trois méthodes principales pour trier des données:

  • L'instruction sorted
  • La méthode sort pour les conteneurs (list, tuple) .
  • La méthode __lt__ à implémenter

L'instruction  native 'sorted' qui permet de trier n'importe quelle collection à partir du moment où elle est:

  • Homogène: les éléments sont de même nature.
  • Itérable : elle peut délivrer les données  les unes après les autres. 
  • Il existe un dispositif connu de comparaison des données deux à deux

Démonstration:


Pour pallier aux différents cas de figure, l'instruction sorted introduit un paramètre supplémentaire: 'key' qui permet d appliquer une fonction appelée au moment du classement des données.
ici on peut ajouter: key=str   qui va transformer chaque item en string (sans impact pour un item déjà de cette classe) 

La solution la plus souple est d'utiliser une fonction lambda pour réaliser des personnalisations de tri complexes: exemple pour  faire  passer les lettres avant les chiffres.


(le symbole '|' a un code ascii inférieur à 'a')  Ici on ajoute '|' avant le resultat de la fonction 'str'.

Enfin, il est possible d'implémenter la méthode __lt__ d'un objet.

exemple ici où une classe implémente la méthode 'lt' basée sur l'identifiant des objets.



Avec comme résultat d'un tri:

Les fonctions de classement utilisent souvent les methodes: getattr pour un objet et operator.itemgetter  pour les autres conteneurs. Pour trier sur le 2 eme élément d'un tuple:



Et avec Golang ?


La prise en charge des tris ressemble au dernier exemple.
Le package 'sort' apporte des utilitaires pour trier la plupart des types de base.

Il s'utilise en l'état pour des tris basiques :

a := []int{3, 6, 4, 1}
    fmt.Println("avant", a)
    sort.Ints(a)
    fmt.Println("après", a)


avant [3 6 4 1]
après [1 3 4 6]

Si on reprend les exemples des autres posts:
avec un dé pipé: 
type DePipe struct {
    De
    Faceplus int
}

Après une série de tirage , on obtient 
face: 1 score: 16.00
face: 2 score: 14.20
face: 3 score: 15.50
face: 4 score: 13.60
face: 5 score: 13.60
face: 6 score: 27.10

Pour trier sur la fréquence, on utilisera  SliceStable du package sort.

De cette façon:

sort.SliceStable(tface, func(i, j int) bool { return tirages2[tface[i]] < tirages2[tface[j]] })

Si on souhaite quelle chose de plus souple, par exemple pour trier un tableau avec de nombres et des chaines. On peut définir un tableau acceptant tous les types et implémenter les fonctions de l'interface sort:(len , swap, less)
type Maliste []interface{}

func (a Maliste) Len() int      { return len(a) }
func (a Maliste) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a Maliste) Less(i, j int) bool {
    si := fmt.Sprintf("%v", a[i])
    sj := fmt.Sprintf("%v", a[j])
    if _, ok := a[i].(int); ok {
        si = fmt.Sprintf("|%v", a[i])
    }
    if _, ok := a[j].(int); ok {
        sj = fmt.Sprintf("|%v", a[j])
    }

    return si < sj
}

Utilisation:

g := Maliste{3, 6, "a", 4, 1}
    fmt.Printf("g avant tri: %T, %v\n", g, g)
    sort.Sort(g)
    fmt.Printf("g apres tri: %T, %v\n", g, g)


Ici dans la fonction less , comme dans l'exemple Python , j'ajoute un caractère dans un entier afin de faire passer les lettres avant les chiffres. Avec comme résultat:

g avant tri: main.Maliste, [3 6 a 4 1]
g apres tri: main.Maliste, [a 1 3 4 6]

Inverser l'ordre d'un tri.


En Python, l'option reverse permet de sélectionner l'ordre descendant:
 Exemple:

Pour le Go : c'est la fonction sort.Reverse qui inverse l'ordre.
sort.Sort(sort.Reverse(g))

g apres tri: main.Maliste, [a 1 3 4 6]
g apres inversion tri: main.Maliste, [6 4 3 1 a]


Voila pour les opérations basiques de tri.