Le langage Go Un langage de programmation imprative

  • Slides: 45
Download presentation
Le langage Go Un langage de programmation impérative et concurrente CSI 2520

Le langage Go Un langage de programmation impérative et concurrente CSI 2520

Le langage Go � Développé en 2007 -2009 chez Google par 3 ingénieurs sous

Le langage Go � Développé en 2007 -2009 chez Google par 3 ingénieurs sous leur 20% en libre travail: ◦ Robert Griesemer ◦ Rob Pike ◦ Ken Thompson (un des inventeurs de C) � Lancement publique le 8 janvier 2010 ◦ en logiciel libre ◦ golang. org � Premier langage du 21ème siècle CSI 2520

Le langage Go � Go combine ◦ Compilation rapide (comme Java) ◦ Éxécution rapide

Le langage Go � Go combine ◦ Compilation rapide (comme Java) ◦ Éxécution rapide (comme C/C++) ◦ Facilité de programmation (comme Python) � Go est un langage avec des types forts et une mémoire protégée ◦ Pas possible d’utiliser plusieurs types dans une expression ◦ Pas d’arithmétique des pointeurs ◦ Avec un ramasse-miette CSI 2520

Paradigme Go � Langage impératif � Avec des éléments de programmation concurrente intégrés ◦

Paradigme Go � Langage impératif � Avec des éléments de programmation concurrente intégrés ◦ Programmation réseau � Pas orienté-objet ◦ Mais avec interfaces, méthodes et polymorphisme � Aussi un langage fonctionel ◦ Autorise les fonctions lambda CSI 2520

Concepts absents � Pas � Pas de surcharge de fonctions de conversions implicites de

Concepts absents � Pas � Pas de surcharge de fonctions de conversions implicites de classes! de types paramétrisés d’exceptions d’assertions CSI 2520

Exécution Go � Environ 20% plus lent que C � 2 x plus rapide

Exécution Go � Environ 20% plus lent que C � 2 x plus rapide que Java et 70% moins gourmand en mémoire CSI 2520

Éléments Go � Code ◦ ◦ facile à lire, structuré en package Dont un

Éléments Go � Code ◦ ◦ facile à lire, structuré en package Dont un package main Un package peut avoir une fonction init() Utilise le Unicode Sensible à la casse � Nombreux concepts orthogonaux � 25 mots-clé � 36 identificateurs prédéfinis ◦ Types et fonctions de base CSI 2520

Un petit programme Go package main import "fmt" // importation func main() { fmt.

Un petit programme Go package main import "fmt" // importation func main() { fmt. Println("Hello le monde") } // débuter par une lettre majuscule // donne à une fonction une visibilité // externe au package (exportation) CSI 2520

Types � int, int 8, int 16, int 32 � byte, uint 16, uint

Types � int, int 8, int 16, int 32 � byte, uint 16, uint 32, uint 64 � float 32, float 64 � complex, complex 64, complex 128 � bool � String � et les pointeurs… CSI 2520

Les variables et fonctions en Go package main import ( "fmt" ) const pi=

Les variables et fonctions en Go package main import ( "fmt" ) const pi= 3. 1416 // declaration du type optionnel: const pi float= 3. 1416 var x int = 5 // variable globale func main() { var ( // declaration en groupe a float 64 = 8. 8 b float 64 ) b= fonction(a) fmt. Printf("valeur: %f", b) } func fonction(z float 64) float 32 { u : = 3. 3 // declaration initialisee return u*z } CSI 2520

Fonctions à plusieurs retours func main() { var s int var d int s,

Fonctions à plusieurs retours func main() { var s int var d int s, d = plusmoins(7, 9) fmt. Printf("resulat= %d et %d", s , d) for i, j: = 1, 5 ; j<100 ; i, j= i+1, j+5 { fmt. Printf("%d et %d", i , j) } } // une fonction peut retourner plus d'une valeur func plusmoins(a int, b int) (somme int, difference int) { somme= a+b difference= a-b return } CSI 2520

Fonction avec code d’erreur func imc(taille float 64, poids float 64) (float 64, bool)

Fonction avec code d’erreur func imc(taille float 64, poids float 64) (float 64, bool) { if taille > 0. 0 { return poids / (taille*taille), true } else { return 0. 0, false } } CSI 2520

Test valeur de retour // if initialisaton; condition if valeur, ok : = imc(1.

Test valeur de retour // if initialisaton; condition if valeur, ok : = imc(1. 50, 55); ok { fmt. Printf("valeur: %fn", valeur) } CSI 2520

Fonction en paramètre type Point struct { x float 64 y float 64 }

Fonction en paramètre type Point struct { x float 64 y float 64 } func Distance(p 1 Point, p 2 Point) (distance float 64){ distance = math. Sqrt(math. Pow(p 1. x - p 2. x, 2. 0) + math. Pow(p 1. y - p 2. y, 2. 0)) return } func calc(p 1 Point, p 2 Point, d func(Point, Point)(float 64) { return d(p 1, p 2) } CSI 2520

Fonction lambda func main() { a : = Point{2. , 4. } b :

Fonction lambda func main() { a : = Point{2. , 4. } b : = Point{5. , 9. } dist : = calc(a, b, Distance) fmt. Printf("resulat= %fn", dist) dist = calc(a, b, func(p Point, q Point)float 64{ // definition d’une fonction lambda return math. Abs(p. x-q. x)+math. Abs(p. y-q. y)}) fmt. Printf("resulat= %fn", dist) } /* Affectation: d 1: = func(p Point, q Point)float 64{ return math. Abs(p. x-q. x)+math. Abs(p. y-q. y)} Appel: func(p Point, q Point)float 64{ return math. Abs(p. x-q. x)+ math. Abs(p. y-q. y)}(p 1, p 2) */ CSI 2520

Les pointeurs func main() { var p *int // pointeur var i int i=

Les pointeurs func main() { var p *int // pointeur var i int i= 7 ptr: = &i *ptr= i+5 // déréférence fmt. Printf("resulat= %dn", i) }

Construction des structures type Point struct { x int y int } var (

Construction des structures type Point struct { x int y int } var ( p 1= Point{1, 2} p 2= Point{y: 7} pp= &Point{3, 4} // pointeur a un Point ) func main() { // allocation dynamique ptr 1: = new(Point) ptr 2: = &Point{9, 8} }

Pointeurs et structures type Point struct { x int y int } func main()

Pointeurs et structures type Point struct { x int y int } func main() { un : = Point{8, 1} complement(&un) fmt. Printf("resulat= %d et %dn", un. x , un. y) } func complement(p *Point) { // operateur de de-reference non-requis p. x, p. y = -p. y, -p. x } CSI 2520

Les fabriques type point struct { x int y int } // point est

Les fabriques type point struct { x int y int } // point est maintenant privé // les autres packages doivent // utiliser la fabrique func main() { p 1 : = New. Point(1, 2) fmt. Printf("point= %vn", p 1) // point= &{1 2} } func New. Point(i, j int) *point { p: = new(point) p. x, p. y = i, j return p }

Tableaux en Go package main import "fmt" Copie d’un [5]int dans un [5]int func

Tableaux en Go package main import "fmt" Copie d’un [5]int dans un [5]int func moyenne(tab [5]int) (moyenne float 64){ // for index, valeur : = range collection for _, valeur : = range tab { moyenne+= (float 64)(valeur) } moyenne /= (float 64)(len(tab)) return } func main() { La variable tableau est de type [5]int // le tableau est un type var tableau = [5]int{3, 4, 8, 9, 2} m : = moyenne(tableau) // passage par valeur fmt. Printf("resulat= %fn", m) } CSI 2520

Slices en Go � Un ‘slice’ est une référence à un segment contigue d’éléments

Slices en Go � Un ‘slice’ est une référence à un segment contigue d’éléments dans un tableau � Les slices sont utilises plus fréquemment que les tableaux en Go � Un slice a une dimension et une capacité (et un pointeur à un tableau � Pour les créer: ◦ var slice []int = tableau[start: fin] ◦ slice: : = make([]int, 100) // dimension et capacité ◦ make va créer un tableau et un slice CSI 2520

Tableau dynamique � Les slices peuvents servir à créer des tableaux dynamique ◦ Tableaux

Tableau dynamique � Les slices peuvents servir à créer des tableaux dynamique ◦ Tableaux dont la taille double au besoin t : = make([]byte, len(s), cap(s)*2) copy(t, s) s = t

Exemple avec slices // tab est une slice func moyenne(tab []int) (moyenne float 64){

Exemple avec slices // tab est une slice func moyenne(tab []int) (moyenne float 64){ // for index, valeur : = range collection for _, valeur : = range tab { moyenne+= (float 64)(valeur) } moyenne /= (float 64)(len(tab)) return } func main() { var tableau = [5]int{3, 4, 8, 9, 2} m : = moyenne(tableau[: ]) // tous les elements fmt. Printf("resulat= %fn", m) m = moyenne(tableau[2: ]) // elements 2 a la fin fmt. Printf("resulat= %fn", m) m = moyenne(tableau[1: 3]) // element 1 a 3 (exclu) fmt. Printf("resulat= %fn", m) } CSI 2520

Append et Slices � La fonction append permet d’ajouter des éléments à un slice

Append et Slices � La fonction append permet d’ajouter des éléments à un slice ◦ Au besoin, le tableau correspondant sera ré-alloué afin d’augmenter la capacité du slice var s []int s = append(s, 0) s = append(s, 1) s = append(s, 2, 3, 4)

Lire le clavier package main import "fmt" func main() { var nom string fmt.

Lire le clavier package main import "fmt" func main() { var nom string fmt. Printf("Votre nom? ") fmt. Scanf("%s", &nom) fmt. Printf("n. Bonjour %sn", nom) } CSI 2520

Lire et écrire dans un fichier func Copier. Fichier(dst. N, src. N string) (ecrit

Lire et écrire dans un fichier func Copier. Fichier(dst. N, src. N string) (ecrit int 64, error) { src, err : = os. Open(src. N) if err != nil { return } dst, err : = os. Create(dst. N) if err != nil { src. Close() // fermer le fichier source return // en cas d’erreur } ecrit, err = io. Copy(dst, src) dst. Close() src. Close() return } CSI 2520

Exécution différée � L’énoncé defer permet de différer l’execution d’un bloc jusqu’à la fin

Exécution différée � L’énoncé defer permet de différer l’execution d’un bloc jusqu’à la fin de la fonction qui le contient � Celui-ci est principalement utilisé pour faire du nettoyage avant de quitter la fonction � Les paramètres d’une fonction différée sont évalués lorsque l’énoncé est rencontré � L’exécution de la fonction différée est garantie peu importe comment la fonction qui la contient retourne CSI 2520

Exécution différée func Copier. Fichier(dst. N, src. N string) (ecrit int 64, error) {

Exécution différée func Copier. Fichier(dst. N, src. N string) (ecrit int 64, error) { src, err : = os. Open(src. N) if err != nil { return } defer src. Close() dst, err : = os. Create(dst. N) if err != nil { return } defer dst. Close() return io. Copy(dst, src) } CSI 2520

Pas d’exceptions en Go � Le retour de codes d’erreur rempace les exceptions �

Pas d’exceptions en Go � Le retour de codes d’erreur rempace les exceptions � Lorsqu’une erreur grave se produit, il est possible d’utiliser l’énoncé panic ◦ À n’utiliser que pour les erreurs imprévues ◦ Correspond aux violations d’assertions � En cas de panique la fonction s’interrompt immédiatement, les fonctions différées sont exécutées et retourne à la fonction appelante qui déclenche à son tour une nouvelle panique CSI 2520

Déclencher une panique func main() { fmt. Println("Debut") var des. Mots []string traite(des. Mots)

Déclencher une panique func main() { fmt. Println("Debut") var des. Mots []string traite(des. Mots) fmt. Println("Fin") } func traite(mots []string) int { defer func() { fmt. Println("quelques nettoyages") }() if len(mots) == 0 { // erreur! panic("aucun mot!") } // traitement du tableau de mots. . . return len(mots) } CSI 2520

Résultat de la panique CSI 2520

Résultat de la panique CSI 2520

Recouvrer après panique � L’énoncé recover permet de regagner le controle après une panique

Recouvrer après panique � L’énoncé recover permet de regagner le controle après une panique � À utiliser dans une fonction différée � En l’absence de panique, le recover retourne simplement nil CSI 2520

L’énoncé recover func main() { fmt. Println("Debut") defer func() { if r : =

L’énoncé recover func main() { fmt. Println("Debut") defer func() { if r : = recover(); r != nil { fmt. Printf("Une erreur dans ce package: %v", r) } }() var des. Mots []string traite(des. Mots) fmt. Println("Fin") } CSI 2520

Résultat de recover CSI 2520

Résultat de recover CSI 2520

Méthodes et récepteurs � Une méthode est une fonction agissant sur une variable associée

Méthodes et récepteurs � Une méthode est une fonction agissant sur une variable associée à un certain type (ou structure) ◦ Le type peut être un pointeur (par référence); obligatoire si la méthode modifie les attributs de l’instance � La structure et ses méthodes ne sont pas liés ensemble ◦ pas d’encapsulation comme avec les classes ◦ Les propriétés et les comportements sont des concepts orthogonaux � Elles doivent seulement faire partie du même package CSI 2520

Définition et appel d’une méthode // structure type Point struct { x float 64

Définition et appel d’une méthode // structure type Point struct { x float 64 y float 64 } // methode (ici un pointeur afin d’éviter une copie de l’objet) func (pt *Point) norme() float 64 { return math. Sqrt(pt. x*pt. x + pt. y*pt. y) } func main() { a : = Point{2. , 4. } // appel a la methode n : = a. norme() // traduit en (&a). norme() fmt. Printf("resulat= %fn", n) ptr : = &Point{2. , 4. } // appel a la methode // il y aurait déréférencement automatique si // le récepteur était une valeur m : = ptr. norme() fmt. Printf("resulat= %fn", m) } Go specs: If x is addressable and &x’s method set contains m, x. m() is shorthand for (&x). m() CSI 2520

Encapsulation et exportation Package point // structure type Point struct { // le type

Encapsulation et exportation Package point // structure type Point struct { // le type est exporté x float 64 // mais pas ses attributs y float 64 } // getter func (pt *Point) Get. X() float 64 { // méthode exportée return pt. x } CSI 2520

Interfaces // structure avec type embarque type Point. Colore struct { Point // le

Interfaces // structure avec type embarque type Point. Colore struct { Point // le type Point est embarqué dans le type Point. Colore couleur string } // une autre structure type Boite struct { poids float 64 couleur string } // interface type Coloreur interface { Set. Couleur(string) Couleur() string } CSI 2520

Interfaces � Un type n’a pas besoin de déclarer explicitement qu’il réalise une interface

Interfaces � Un type n’a pas besoin de déclarer explicitement qu’il réalise une interface ◦ Il satisfait implicitement l’interface si il possède les méthodes requises � Un type peut satisfaire plusieurs interfaces � Une interface peut être définit dans un autre package que les types qui la satisfont � Une variable interface peut référer à n’importe quelle instance satisfaisant celle-ci

Satisfaire une interface func (p *Point. Colore) Couleur() string { return p. couleur }

Satisfaire une interface func (p *Point. Colore) Couleur() string { return p. couleur } // doit etre pointeur afin de modifier la couleur func (p *Point. Colore) Set. Couleur(new. Couleur string) { p. couleur = new. Couleur } func (p *Boite) Couleur() string { return p. couleur } CSI 2520

Polymorphisme func main() { var p *Coloreur p= new(Point. Colore) p. Set. Couleur("rose") //

Polymorphisme func main() { var p *Coloreur p= new(Point. Colore) p. Set. Couleur("rose") // pour être accepté, le type // doit définir les méthodes // de l’interface tableau : = [4]Coloreur{ &Point. Colore{Point{1. 1, 2. 2}, "rouge"}, L’utilisation d’une référence (pointeur) est &Boite{32. 4, "jaune"}, obligatoire car c’est le &Point. Colore{Point{1. 1, 2. 2}, "bleu"}, p} pointeur à des Boite qui satisfait l’interface for _, element : = range tableau { fmt. Printf("couleur= %sn", element. Couleur()) } } Un pointeur peut accèder aux méthodes de son type (déréférencement automatique), mais pas l’inverse Go specs: The method set of any other named type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T). CSI 2520

Arbre binaire type Tree struct { Left *Tree Value int Right *Tree } func

Arbre binaire type Tree struct { Left *Tree Value int Right *Tree } func New. Tree(v int) *Tree { return &Tree{nil, v, nil} } func (t *Tree) In. Order() { if t. Left != nil { t. Left. In. Order() } fmt. Println(t. Value) if t. Right != nil { t. Right. In. Order() } }

Insertion dans un arbre binaire de recherche func (t *Tree) Insert(v int) *Tree {

Insertion dans un arbre binaire de recherche func (t *Tree) Insert(v int) *Tree { if v < t. Value { if t. Left == nil { t. Left= New. Tree(v) return t. Left } else { return t. Left. Insert(v) } } else { if t. Right == nil { t. Right= New. Tree(v) return t. Right } else { return t. Right. Insert(v) } } return t } func main() { t: = New. Tree(5) t. Insert(7) t. Insert(9) t. Insert(2) t. In. Order() }

Une file Go générique � Truc: utiliser l’interface vide qui est satisfaite par tous

Une file Go générique � Truc: utiliser l’interface vide qui est satisfaite par tous les types Go ◦ Cette solution est toutefois moins efficace type Queue struct { items []interface{} } // Enqueue adds a value to the // end of the queue func (s *Queue) Enqueue(t interface{}) { s. items = append(s. items, t) } // Dequeue removes a value from the // start of the queue func (s *Queue) Dequeue() interface{} { item : = s. items[0] s. items = s. items[1: len(s. items)] return item }

Une file Go générique // Front returns the item next in // the queue,

Une file Go générique // Front returns the item next in // the queue, without removing it func (s *Queue) Front() *interface{} { item : = s. items[0] return &item } func main() { var queue Queue // Is. Empty returns true if the // queue is empty func (s *Queue) Is. Empty() bool { return len(s. items) == 0 } queue. Enqueue(10) queue. Enqueue(20) queue. Enqueue(30) t: = queue. Dequeue() fmt. Println("value=", t) // Size returns the number of elements func (s *Queue) Size() int { return len(s. items) } queue. Enqueue(40) queue. Enqueue(50) queue. Enqueue(60) t= queue. Dequeue() fmt. Println("value(2)=", t) fmt. Println("size=", queue. Size()) fmt. Println("front=", *queue. Front()) }