Affichage des articles dont le libellé est golang. Afficher tous les articles
Affichage des articles dont le libellé est golang. Afficher tous les articles

dimanche 1 octobre 2023

Les tranches en Python et Golang

 Les tranches ou slice sont présentes en Python sous deux formes:

La forme simple à la volée sous forme de fonction


Sous forme objet avec les slices nommés:

...

Avec  3 attributs et une  méthode spécifique

La méthode indices prend une longueur en paramètre et retourne les limites du découpage (utilité limitée)

Les tranches nommées sont utiles pour découper des lignes possédant des champs de taille fixe.
avec par exemple 
code_postal = [1,7)]
nom = [7,27]
etc ..


En golang.

Les tranches s'appuient sur des tableaux ou à défaut embarque un tableau interne.
Leur définition doit comporter: le type des données, la longueur et la capacité.
tranche_a := make([]int, 3, 6)


(ici longueur = 3 pour une capacité de 6)

Avec quelques pièges: 
tranche_b := tranche_a[1:3]

Si on modifie tranche_b ou tranche_a, les deux seront modifiés

tranche_b[0] = 42

Ce comportement peut évoluer si on ajoute des éléments par la fonction append. Si la longueur dépasse la capacité , automatique le tableau donnera lieu à un nouveau tableau d'un capacité augmentée. 

tranche_b = append(tranche_b, 19)
tranche_b = append(tranche_b, 20)
tranche_b = append(tranche_b, 21)
tranche_b = append(tranche_b, 22)
tranche_b = append(tranche_b, 23)
tranche_b[0] = 99


Dans ce dernier cas tranche_a et tranche_b pointeront vers deux espaces différents.

tranche_a [3 42 0] capacité: 6 taille: 3

tranche_b [99 0 18 19 20 21 22 23] capacité: 10 taille: 8


Pour éviter tout effet de bord:

  • Utiliser la fonction copy qui va dès le départ créer deux zones mémoires distinctes.
  • Utiliser la forme slice[debut:fin:max] pour éviter les effets de bord d'append.



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.

jeudi 24 août 2023

Python et golang : le passage de paramètre


 

En programmation le mode de passage de paramètre pour l'appel d'une fonction (ou d'une méthode) est un point central car il est souvent source de bug. Un paramètre passé à une fonction est avant tout une donnée. Généralement on peut distinguer deux modes de passage:

  • Passer la valeur de la donnée: le paramètre local de la fonction va être construit à partir de la recopie des valeurs de la donnée du bloc appelant.
  • Passer la référence de la donnée : La variable locale ne contiendra pas les valeurs de la donnée de départ mais la référence (adresse) de la donnée. C'est ce qu'on appelle un pointeur.

Une donnée d'un programme recoupe deux choses: une nom de donnée et un espace de stockage contenant la ou les valeurs associées à ce nom de donnée. 

Lorsque qu'on passe un paramètre par valeur, une nouvelle donnée est créée  et son espace associé est alimenté par une copie de la donnée émettrice. Dans le cas d'un passage par référence ou par adresse, le nom de donnée est créée et son espace associé est le même que celui de la donnée émettrice. Les deux données pointent vers le même espace mémoire.

Dans les langages sophistiqués, ce mécanisme est masqué au développeur. Il n'aura pas à se poser la question. Ainsi en Python, c'est l'adresse de la variable qui sera passée comme paramètre à l'appel de la fonction pour les données de type conteneur (list, tuple, set , obj) . C'est le contenu de la variable 'self' d'une méthode Python.

 Exemple pour la fonction suivante en Python:



Avec comme exécution: 

Et un effet de bord pas toujours voulu, illustré par Pythontutor : les deux variables pointent sur la même zone mémoire.

La même fonction appelée avec un tuple donnera un effet différent:




Explication: l'instruction '+='  n'a pas le même comportement en présence d'objet muable (list) qu' immuable (tuple). C'est pourquoi, il faut éviter de passer en paramètre des objets muables à une fonction Python.

Et en Golang ? .

Le Go permet de choisir son mode de passage de paramètre : par valeur (recopie) ou par pointeur (adresse).  Mais il va plus loin que ça. Pour une méthode, il va générer plusieurs signatures: une par valeur et une par pointeur. En fonction de ce que recevra la méthode, la bonne méthode sera appelée.

Exemple si on a codé ceci :

// GetFace: cette fonction/methode retourne le nombre de face du dé.
func (d De) GetFace() int {
	return d.Nbface
}
 
Bien que la signature préfigure une passage par valeur, il sera possible de fournir un pointeur.
Le compilateur aura généré les 2 formes de transmission: par valeur ou par pointeur
d1 := De{6}
    fmt.Printf("%v , %T\n", d1, d1)
    fmt.Printf("nb de face (adresse):%v\n", d1.GetFace())
    fmt.Printf("nb de face (pointeur):%v\n", (&d1).GetFace())
    d1p := &d1
    fmt.Printf("nb de face (pointeur V2):%v\n", d1p.GetFace())

Avec comme résultat:
{6} , libde.De
nb de face (adresse):6
nb de face (pointeur):6
nb de face (pointeur V2):6 
Cette simplification fonctionne dans aussi dans le cas inverse où une fonction doit utiliser un pointeur.

Cette facilité à une contrepartie : des difficultés à comprendre ou à reprendre un code.
Aussi, il est recommandé d'utiliser dans le codage et dans l'exécution le passage par adresse (pointeur)
Car seule l'utilisation des pointeurs permettra de modifier les attributs de la structures. 
Comme ci-dessous:
// methode setter
    d4 := DePipe{
        De{6},
        5,
    }
    fmt.Println("avant appel", d4)
    d4.SetFaceValeur(2)
    fmt.Println("apres appel", d4)
    tmp := &d4
    tmp.SetFacePointeur(3)
    fmt.Println("apres appel", d4)
    (&d4).SetFacePointeur(6)
    fmt.Println("apres appel", d4)

Avec comme exécution:
avant appel {{6} 5}
par valeur {{6} 2}
apres appel {{6} 5}
par pointeur {{6} 3}
apres appel {{6} 3}
par pointeur {{6} 6}
apres appel {{6} 6}

Conclusion: utilisez les pointeurs.

source https://github.com/germanlinux/lemongo/tree/main/lemonlabs/src/pointeurs

samedi 12 août 2023

De Python à Golang: premiers pas

 Passer d'un langage à un autre n'est pas toujours une démarche facile mais constitue un excellent exercice.

Avant de commencer à faire du Go (golang) , il faut retenir 3 choses:

  • La preparation du répertoire projet est importante: on ne peut pas faire un bout de code sur le coin de la table.
  • Il faut un bon IDE : Vscode ou VScodium sont tout à fait adaptés au Go: ils mettent en forme le code automatiquement.
  • La lecture de code Go est le meilleur moyen d'apprendre les bonnes pratiques du langage. Il ne faut pas chercher dans un premier temps à comprendre le code mais plutôt à observer comment le projet s'organise.


Commençons par un exemple simple: Je vais utiliser l'implémentation d'une librairie qui émule le lancer d'un dé en Python pour la transposer en Golang.




Cette classe propose 2 méthodes en plus du constructeur.

En Golang, il n'y pas de mécanisme d'encapsulation comme dans les autres langages, la description de l'objet est portée dans une structure, les fonctions viennent compléter l'ensemble.

type De struct{ Nbface int }
 
 
func (d De) GetFace() int {
	return d.Nbface
}
func (d De) Lance() int {
	n := rand.Intn(d.Nbface) + 1
	return n
}
 
On peut voir que les structures remplacent les classes. Que le 'self' envoyé en coulisse à une méthode Python, prend la forme d'un préfixe de la définition de la fonction en Go.
Comme Go est un langage compilé avec des types de variables statiques, une déclaration du type de la donnée est nécessaire. Mais il existe un sucre syntaxique bien utile l'opérateur ':='. Le type de la donnée d'affectation sera déduit à partir de la partie droite : signature de la fonction, type de la donnée reçue etc.

2 remarques: un élément déclaratif: type , nom de variable, conteneur, fonction etc.. sera utilisable en dehors de son fichier source (exportable) qu'a la condition que son nom commence par une lettre MAJUSCULE.

Utilisation de notre librairie


En python et en Go : par un import.

En python : from libde import De

Puis
En Go :



package main
 
import (
	"fmt"
	"lemonlabs/src/de/libde"
	"sort"
)
 
type De = libde.De
 
func main() {
 
	d1 := De{6}
	fmt.Printf("%v , %T\n", d1, d1)
	fmt.Printf("nb de face :%v\n", d1.GetFace())
	n1 := d1.Lance()
	fmt.Printf("face :%v", n1)
Exécution: go run .\main.go

{6} , libde.De
nb de face :6
face :3

Heritage vs composition.

Soit une classe Depipe qui permet de doubler les chances de sortie d'une face particulière.

En Python, on pourra utiliser les propriétés de l'héritage:

En Go, on utilisera la composition:


type De struct{ Nbface int }
 
type DePipe struct {
	De
	Faceplus int
}
 
A l'exécution de ce code:
d2 := DePipe{
        De{6},
        6,
    }
    fmt.Printf("%v , %T\n", d2, d2)
Avec le résultat suivant:

{{6} 6} , libde.DePipe

Par contre il faudra redevelopper la fonction getface pour depipe au moins d'utiliser une interface. 
(voir les prochains articles)

La suite des fonctions en Go



func (d DePipe) GetFace() int {
	return d.De.Nbface
}
func (d DePipe) Lance() int {
	n := rand.Intn(d.De.Nbface+1) + 1
	if n == d.De.Nbface+1 {
		n = d.Faceplus
	}
	return n
}

Remarque:  la structure qui sera passée aux fonctions Go (GetFace ou Lance) , sera une copie de la structure appelante  (par valeur) . 

Voici pour une première introduction, le source est disponible sur github .
Dans ces exemples, les champs ne sont pas mis à jour. Pour modifier un champ  d'une structure Go, il faudra utiliser un pointeur (dans les prochains articles) .

jeudi 3 août 2023

Collecte sur le web (3)

 A nouveau une liste de liens relatifs à Python, Golang , webSSO et divers sujets.

Python

Divers

Cloud - IAM

Outils de présentation

Langage GO - golang