Cours langage GO - annotation - Anthony Le Goff
#INTRO ALGORITHME SOUS GO De l'écriture du premier programme à la logique informatique golang
Premier programme et présentation de "hello world"
code hello.go
package main
import "fmt" // Implémentation formatage I/O module package main
/* Début du bloc avec accolade fonction main afficher qqchose */
func main() {
fmt.Printf("Hello, chrome os\n have fun こんにちは世界\n")
}
sous UNIX Shell (crosh
exemple après shell access mode développeur)
- Télécharger la dernière version
sur le site golang. Linux amd64 et extraire
l'archive
tar.gz
[gzip] avec la commandetar -xzf <fichier.tar.gz>
-xzf
est le flag de la commande tar
, plus d'information shell tar --help
Vous pouvez faire le téléchargement en ligne de commande via
wget url/fichier.tar.gz
Vérifier le hashage de l'archive, si les données ne sont pas corrompu durant
le téléchargement algorithme cryptographique sha1
.
sha1sum fichier.tar.gz
La suite de chiffre / lettre doit correspondre avec le lien sur le site / server
Enfin décompresser l'archive dans /usr/local via la commande
sudo tar -C /usr/local -xzf ~/Downloads/<fichier>.tar.gz
Une fois installer en lançant /usr/local/go/bin/go
accès aide cmd go
-
Créer dossier de travail
gocode
inclus dossierbin
pour binaire etsrc
pour code source. Ce positionner dans dossier travail avec la commandecd
-
Configurer Gopath
Gopath permet de gérer la persistance redémarrage et la gestion package et de
les installer dans le système. Via éditeur ligne de cmd nano
ou vi
:
$ vi ~/.bashrc
Ajoutez en mode insertion vim
en tapant i
export PATH=$PATH:/usr/local/go/bin
export GOPATH=~/Downloads/gocode
export PATH=$PATH:$GOPATH/bin
sudo mount -i -o remount,exec /home/chronos/user/
Sauvegardez et quitter le fichier en lançant échap
et cmd :wq!
sous vim
Redémarrer ou taper ces 4 lignes commandes dans le shell prise en compte immédiate
- Compile et test dans
src
viago build hello.go
puis./hello
- Installer package créer le dossier dans
src
puisgo install hello
& lancer en faisanthello
La programmation en GO est un langage informatique développé à l'origine par Robert Griesemer, Rob Pike, Ken Thompson, Ian Taylor, Google en 2007. Le public qui est familiarisé avec certain type de langage tels que:
- C
- C++
- Perl
- Java
- Erlang
- Scala
- Haskel
Aurons moins de mal à appréhender la programmation Go dont il est inspiré.
Ex: Docker est un programme jeune et populaire développé en Go sous Github.
Go est un langage orienté pour "systèmes et les réseaux" prisé par le C & C++ .
Cela n'empêche pas de développer en Go web server ou encore apps smartphone. Il
est possible de trouver bien de la documentation en Go sur le site officiel à
l'adresse http://golang.org/doc/ Egalement la documentation spécifique est
disponible sous un programme nommé godoc
exemple d'utilisation avec les
opérateurs et les fonctions built-in:
% godoc builtin
/*ou en local dand le navigateur via shell:*/
% godoc -http=":6060"
/*Puis dans la barre du navigateur:*/
http://localhost:6060/pkg/builtin
Go est-il un langage orienté-objet? Oui et non à la fois, Go introduit la notion d'interface qui est un ensemble de méthodes, une fonction peut être attachée à un type, ou encore l'héritage multiple ainsi que pour définir une structure de Tache grâce au constructeur.
Clair et Simple Go à pour but en quelques lignes de code de pouvoir faire beaucoup de chose
Concurrent Go permet de parallelisé des taches à l'intérieur d'espace d'adressage, à travers des processus et des coeurs multiple de processeur grâce au goroutine
Canaux La communication à travers des Goroutines est faîte via canal
Rapidité La compilation est rapide, plus rapide que la compilation en C mesuré en Seconde.
Format standard
La règle est simple, la sortie du filtre gofmt
est le format officiel
Type postfix
Le Type est donnée après le nom variable comme var a int
au lieu de
int a;
comme on retrouve en C.
UTF-8 UTF-8 est partout, dans les strings et le code. Final il est possible utiliser dans le code source Φ = Φ + 1
Open Source La licence Go est entièrement open source, voir le fichier LICENSE dans le code source de la distribution Go.
La langage Go est abordé en parallèle à introduire l'algorithme informatique au niveau des notes prise pour le lecteur. L’algorithmique est l’ensemble des règles et des techniques qui sont impliquées dans la définition et la conception d’algorithmes, c’est-à-dire de processus systématiques de résolution d’un problème permettant de décrire les étapes vers le résultat. En d’autres termes, un algorithme est une suite finie et non-ambiguë d’instructions permettant de donner la réponse à un problème.
L’information est la matière première de l’informatique . Les algorithmes, qui sont constitués d’informations, stockent, manipulent et transforment d’autres informations, à l’aide de machines traité sous forme de programme avec son code source. Tout, en informatique, est représenté comme une séquence de 0 et de 1 : les algorithmes, ou plutôt les programmes,le contenu d’un livre, une photo, une vidéo...
La base binaire est le langage machine, il ne comprend que des suites de 001011 il y a une raison théorique et technique de l'utilisation du binaire:
- On ne peut pas faire plus simple que 2 symboles (0, 1) avec un symbole plus rien ne fonctionne
- Les symboles 0 et 1 sont transposables électroniquement par: le courant passe ou ne passe pas. Réprésente donc toutes les informations sous forme de bits ( binary digit = chiffre binaire). La quantité d'information est justement le nombre de bit nécessaires pour représenter cette information.
- Le stockage de l'information binaire progresse niveau technique et l'utilisation du Qbit comme base computation ou le spin de l'électron dans la théorie quantique est "up" = 1 ou "down" = 0 ou l'Etat du système admet une information intriquée.
- Le bit (binary digit) est l'unité d'information: par 0 ou 1
- L'octet est un groupe de 8 bits (octet en anglais = bytes)
La mémoire (disque dur, mémoire flash...) sa taille est indiqué en système SI internationnal multiplicateur 10³ (symbole k,M,G,T pour kilos, Mega, Giga et Tera) et pour les multiplicateurs 2¹⁰ etc symbole Ki, Mi, Gi,etc pour kibi, mébi et gibi particulièrement en informatique. Base 2.
Les bases ont retrouve plusieurs familles et conversion en informatique:
- Décimale base 10 (0,1,2,3,4,5,6,7,8,9)
- Binaire base 2
- Hexadécimal base 16 (0,1,2...A,B,C,D,E,F)
Point a abordé et la notion de machine introduit par la cybernétique et englobant un domaine bien plus large tels que la théorie des systèmes commencé année 1950 avec von bertalanffy sur les systèmes biologiques. Trois directions:
- Le systémisme qui apporte le principe de complexité
- Un autre systémisme abordé sur quelques choses de plus vague, sur quelques vérité holistiques sans devenir opérantes (ni réelle, ni formel, ambigu & fantome)
- Il y a le system analysis qui est le correspondant systémique de l'engineering cybernétique en opération réductrice mais transdisciplinaire.
La programmation évolue et l'on voit l'accès à l'informatique quantique grand
public. Dans cet évolution une théorie de l'information quantique a évolué
de l'information classique et l'avènement de l'utilisation du Qbit quantum-bits
.
Le caclcul quantique via RF SQUID et l'information quantique appartient
notion de super-information, de paires d'attributs non discernables. Il
y a impredictibilité de processus déterministes. La cohérence de la paire
d'attribut admet une information localement inaccessible.
Pour plus d'information sur la programmation informatique quantique un détour sur le langage Quipper.
Pour introduire les bases et les types petit rappel sur l'entier et sa définition par la variable int
Type int
>>> 2009 # décimal
2009
>>> 0b11111011001 # binaire
2009
>>> 0o3731 # octal
2009
>>> 0x7d9 # hexadecimal
2009
Important que de ce rappeler que les bases ont un préfixe 0b, Oo, 0x
En langage Go la syntaxe ce réfère énormément au langage C avec un point qui rend fou les codeurs pour débuggué le programme: la fin du ";" et le calvaire trouver celui qui était manquant dans le programme le rendant impossible à compiler. La différence majeur au niveau des variables par rapport aux autres langages, elle est specifié après le nom variable tels que:
Pas int a, mais plutôt a int
Donc après var a int sa valeur assigné est de 0. Avec var s string la valeur zéro de la string vaut "". Déclaré et assigné en Go est deux process.
Déclaration avec =
var a int
var b bool
a = 15
b = false
Déclaration avec :=
a := 15
b := false
L'utilisation de := est essentiellement pour les variables déclarés dans les
fonctions. Multiple déclaration de variable peuvent être groupé et const
&
import
autorise cela. Ecriture:
var (
x int
b bool
)
Multiple variable peuvent être assigné sur une ligne également:var x,y int
Ou encore un assignement en parallèle:
a, b := 20, 16
Les booléens sont de type bool et prennent la valeur de test de condition tout ou rien "TOR" true ou false Les variables numériques, il faut prendre en compte le cas des virgules flottantes. La déclaration de nombre réel à virgule flottante ce fait de cet manière soit en float32 ou float64 au niveau du type au besoin de l'architecture.
// déclaration de la constante Pi
const Pi = 3.14159
// déclaration variable rayon en virgule flottante
var rayon float32 = 1.0
L'un des principaux buit-in type est string. Affectation valeur chaine de
caractère ce fait de cet façon rapide s := "Hello World!"
. En Go string
prend une valeur d'encodage en UTF-8 entre double citation. Un petit exemple
pour changer la première valeur de la string "Hello World" en Go.
s := "hello World"
c := []rune(s)
c[0] = 'c'
s2 := string(c)
fmt.Printf("%s\n", s2)
Explication:
- Converti s en une array de runes
- Change le premier élement de cet array
- Créer une nouvelle string s2 avec modification
- Enfin print la valeur de la string dans fmt.Printf
Multiple string
Ecrire des strings et assemblage sur plusieurs lignes:
s := "Starting part" +
"Ending part"
// OU utilisant raw string et (`)
s := `Starting part
Ending part`
Rune est un alias pour int32 encodé en UTF-8. Permet d'instancer, itération de caractère de string. Possible de faire loop sur chaque octets. Problème encodage string en 8-bit ASCII ne sont pas intégré en Go.
Go a en natif le support des nombres complexes sans librairies. Le type de variable
est nommé complex128
(64 bit réel et partie imaginaire) ou complex64
(32 bit réel et partie imaginaire). Règle:
Complexe écrit comme re + imi ou
re
est partie réel etim
imaginaire
var c complex64 = 5+5i;fmt.Printf("Value is: %v", c) // %v défault format valeur
/* Après compilation et lancement */
print: (5+5i)
Dans des programmes il est nécessaire de reporter des erreurs, le built-in Go
permettant cela s'appel error et ce déclare var e error
valeur 'nil'. Ce
type est une interface.
Opérateur arithmétique
20 + 3 # 23
20 - 3 # 17
20 * 3 # 60
20 / 3 # 6.666666666666667
20 % 3 # 2 (modulo)
abs(3 - 20) # valeur absolue
Type booléen
Deux valeurs possibles : False, True.
• Opérateurs de comparaison : == (egal), != (strict different), >, >=, < et <= :
2 > 8 # False
2 <= 8 < 15 # True
/*Cette optimisation est appelée « principe du shortcut »*/
(3 == 3) || (9 > 24) # True (dès le premier membre)
(9 > 24) && (3 == 3) # False (dès le premier membre)
/* Opérateurs logiques */
&& ET logique,
|| OU logique,
! négation logique
Opérateur bit à bit
& ET logique bit à bit,
| OU logique bit à bit,
^ OU EXCLUSIF bit à bit,
~ complément binaire ou négation bit à bit,
/* opérateurs il faut ajouter*/
<< décalage binaire à gauche
>> décalage binaire à droite
L’algorithmique est bien plus ancienne que l’informatique, que l’ordinateur, et que le langage Go, utilisé dans ces notes. Les exemples les plus anciens et célèbres sont :
- les calculs d’impôts Babyloniens (il y a 4000 ans)
- le calcul du plus grand diviseur commun (Euclide, vers −350)
- les première méthodes de résolution systématique d’équations (Al Khawarizmi, ixe siècle)
Les algorithmes étaient donc d’abord utilisés «à la main». Aujourd’hui on entend généralement par algorithmique la réflexion préliminaire à l’écriture d’un programme d’ordinateur, c’est à dire la recherche d’une méthode systématique et non ambiguë pour résoudre un problème. C’est la partie conceptuelle de la programmation, l’abstraction 1 d’un programme d’ordinateur. L’algorithmique est parfois considérée comme une branche des mathématiques, parfois comme une branche de l’informatique. Les notions d’algorithme, puis d’ordinateur ont été formalisées dans les années 30 et 40 par : Kurt Gödel, Alan Turing, John von Neumann... Avoir des notions en algorithmique permet de développer soi-même des programmes, et/ou d’avoir une discussion constructive avec une équipe de développeurs. Les compétences en algorithmique font aussi partie du bagage minimum d’un scientifique, qu’il soit technicien, chercheur, ingénieur ou simplement curieux.
Ecriture exemple Algorithme d'Euclide
Étant donnés deux entiers, retrancher le plus petit au plus grand et recommencer jusqu’à ce que les deux nombres soient égaux. La valeur obtenue est le plus grand diviseur commun.
Dans cet idée, on introduit le principe des structures de controle c-a-d
on repart sur la base si et sinon [if, else]
permettant de faire des boucles. Le
while équivaut à "répéter" en français disparait en langage Go au profit de
for et switch
/*
----------------------
| ALGORITHME EUCLIDE |
----------------------
E1: Formuler l'idée
___________________
Étant donnés deux entiers, retrancher le plus petit au plus
grand et recommencer jusqu’à ce que les deux nombres
soient égaux. La valeur obtenue est le plus grand diviseur
commun.
E2: Décrire étapes
__________________
a,b : deux nombres
Répéter tant que a et b sont différents :
si le plus grand est a :
les deux nombres deviennent a−b et b
sinon (b est donc le plus grand) :
les deux nombres deviennent a et b−a
le pgcd est a
Les espaces equivaut à l'indentation, ici = 4 (test et
répétition de tâche)
E3: Algorithme formel
_____________________
fonction pgdc(a,b : entiers)
repeter tant que a/=b
si a>b
a=a−b
sinon
b=b−a
retourner a
On retrouve premières bases algo avec les boucles (while) et
test de condition (if, else) Notons que en Go While n'existe pas.
Il faut donc remplacer par une boucle for et l'accolade = retourner a
---------------------------------------
*/
/* E4: Ecriture programme principale calcul plus grand
dénominateur commun */
package main
import "fmt"
func main() {
a := 933
b := 789
for a != b {
if a > b {
a = a - b
} else {
b = b - a
}
}
fmt.Println("le PGDC vaut", a,":", b)
}
La valeur retourner de votre programme après compilation et édition:
Sauvegarder pgdc.go puis dans le dossier de travail lancer go build pgdc.go
enfin pour lancer l'executable bin taper shell ./pgdc
Valeur de sortie: le PGDC vaut 3 : 3
Dans ce programme nous avions déclaré deux entiers a = 933 et b = 789. Le plus
petit diviseur commun valant donc 3 par l'analyse algorithmique d'Euclide. La
difficulté ici resulte à print
via un Println
de fmt.
Cett fonction formate de manière auto tout type paramètre. Retourne la chaine
formatée au lieu de l'afficher équivaut à utiliser formatage défaut %v
. Le
formatage de texte est le module fmt.
Programmer est l’activité qui consiste à :
- traduire des algorithmes dans un langage de programmation :
- afin d’obtenir des réponses effectives (souvent numériques) à des problèmes ;
- afin de se distraire...
- corriger des erreurs dans un programme ;
- rester calme...
Pour aller plus loin l'idéal programme pouvoir insérer une valeur d'entrée à a & b par l'utilisateur et comparer nombres déterminant un pgdc. Non appliqué dans l'exemple mais
scanf
du module fmt et l'opérateur&
le font.
Il est courant d'utiliser controle d'état return
ou break
à la fin tels que:
if err := Chmod(0664); err != nil { // nil is like C’s NULL
fmt.Printf(err) // erreur limité au corps du if
return err
}
On peut utiliser les opérateurs logiques dans les structures de contrôle:
if true && true {
fmt.Println("true")
}
if ! false {
fmt.Println("true")
}
GO utilise le goto
permet de sauter à un label prédifini dans la fonction
courante tels que pour un loop:
func myfunc() {
i := 0
Here: // label du goto LOOP
println(i)
i++ // iterateur compteur incrémentation
goto Here // Jump
}
En langage Go le for
s'utilise sous 3 formes:
for init; condition; post { } // Comme en C for
for condition { } // Comme un while
for { } // boucle infini
Example sous l'écriture en C for
en Go et incrementation compteur
sum := 0
for i := 0; i < 10; i++ { // init; condition; post
sum += i // reduction écriture de sum = sum + i
} // i cesse d'exister à la fin de la boucle
Range s'utilise dans le cadre d'un loop pour slices (tranches), arrays
(tableau), strings, maps, et canaux. range
est un itérateur qui appel paire
de clé. Quand il boucle sur slice ou array, il retourne l'index dans le slice
comme une clé et sa valeur.
Go mot-clé switch
est flexible. Pas besoin de constante ou entiers, c'est un
comparateur de valeur via le mot-clé case
et default
simplifie:
if i == 0 {
fmt.Println("Zero")
} else if i == 1 {
fmt.Println("One")
} else if i == 2 {
fmt.Println("Two")
} else if i == 3 {
fmt.Println("Three")
} else if i == 4 {
fmt.Println("Four")
} else if i == 5 {
fmt.Println("Five")
}
Ce programme indique le nom anglais associé à un nombre via switch
cela donne
switch i {
case 0: fmt.Println("Zero")
case 1: fmt.Println("One")
case 2: fmt.Println("Two")
case 3: fmt.Println("Three")
case 4: fmt.Println("Four")
case 5: fmt.Println("Five")
default: fmt.Println("Unknown Number")
}
Les built-in sont des fonctions inclus sans besoin de faire appel à des librairies ou des packages.
- close utilisé canal de communication, ferme un canal
- delete utilisé pour supprimer entrée maps
- len & cap sont utilisés pour retourner longueur d'un string ou tableau et d'un slice
- new permet d'allouer de la mémoire pour utilisateur défini types données
- ** make** Utiliser allouer mémoire built-in types (maps, slices, canaux)
- copy permet de copier un slice
- ** append** concaténation de slice
- panic, recover comme mécanisme d'exeption
- print, println fonction d'impression bas niveau sans "fmt" debugging
- complex, real, imag utilisation des nombres complexes
Un tableau array
de valeurs est une série consécutive de valeurs, accessibles
par leur indice entier, 0,1,2,3... En Go on déclare un tableau en faisant précéder
le type des éléments par la taille en crochets var x [5]int
donc x nom
du tableau composé de 5 entiers. Test de programme tableau
package main
import "fmt"
func main() {
var x [5]int
x[4] = 100 // Allouer 100 à l'élément 4 du tableau
fmt.Println(x)
}
Afficher valeur print
[0 0 0 0 100]
Un tableau commence toujours par la valeur 0,1,2,3,4..... Zéro est inclus
Ici nous allons abordé les tableaux par le calcul de la somme arithmétique de valeur pour diviser par le nombre de valeur, équivaut nombre élément dans le tableau.
package main
import "fmt"
func main() {
var x [5]float64
x[0] = 17.5
x[1] = 11
x[2] = 9
x[3] = 14.5
x[4] = 13
var total float64 = 0
for i := 0; i < 5; i++ {
total += x[i] // Incrémente les valeurs du tableau x sur chaque élément
}
fmt.Println(total / 5) // Total sur le nombre de valeur
}
Quelques explications sur l'utilisation du tableau
- On déclare un tableau de 5 élément en virgule flottante
- Allocation pour chaque élément une valeur = nos notes
- Déclare variable total en virgule flottante entière
- Boucle for et init, condition, post (incrémente supérieur)
- Calcul bloucle for total incrémenté à chaque élément du tableau x
- Println du total divisé par 5 (nombre de valeur)
A partir de cet compréhension du tableau, on va amélioré le code de manière plus réduite (less is more) en place et optimiser le calcul de valeur moyenne. Nouveau programme
package main
import "fmt"
func main() {
xs := []float64{17.5,11,9,14.5,13} // composite literale
total := 0.0
for _, v := range xs {
total += v
}
fmt.Println(total / float64(len(xs)))
}
- On découvre une nouvelle manière d'interprété un tableau sur une ligne
- Une boucle for "_" (underscore) à la place "i" car non appelé itérateur + range
- Conversion tableau défaut int println en float64 sur un len équivaut taille du tableau
Un Slice est un seqment d'un tableau array
qui est indexable et a une longueur.
slice != array la longueur est autorisé à changer pour le slice
Prenons l'exemple var x []float64
entre crochet valeur longueur prend 0
La taille du tableau est fixe sur une valeur [] donc créé un slice pour
le modifier en utilisant le built-in make
x := make([]float64, 5)
Il y a un troisième paramètre. Correspond à la capacité du tableau dont le slice pointe.
x := make([]float64, 5, 10)
Dans cet exemple len(x) = 5 et cap(x) = 10
La règle qui différencie Array &
Slice est donc que:
len(slice)== n ;
cap(slice)== m ;
len(array)== cap(array)== m .
Une autre manière de créer un slice est en utilisant expression [low : high]
arr := []float64{1,2,3,4,5}
x := arr[1:4]
Même si le tableau a 5 élément, notre slice défini [1:4] retourne
[2,3,4] Ainsi arr[0:]
équivaut à arr[0:len(arr)]
et arr[:5]
correspond à
arr[0:5]
Si vous avez besoin d'étendre un slice il y a des built-in fait pour ça tels que
append
& copy
. Append alloue un nouveau slice dans le slice éxistant
s0 := []int{0, 0}
s1 := append(s0, 2)
s2 := append(s1, 3, 4, 5, 7)
s3 := append(s2, s0...) // Noter 3 petits points
s3 retourne s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
La fonction copy éléments de slice d'une source src
à la destination dst
et retourne le nombre d'élément copié.
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
n1 := copy(s, a[0:]) // n1 == 6,s == []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])
Le type map ce retrouve dans plusieurs langage. En C++ c'est la même chose,
en Perl on trouve hashes et en Python les dictionnaires. Map est une collection
de paire de clé en désordre. Notion de clé associée. Prenons exemple
x["clé"] = 10
et supprimons la valeur de la clé.
x := make(map[int]int)
x[1] = 10
fmt.Println(x[1])
Quand on Print la valeur de clé "1" on obtient 10. Et pour supprimer la clé:
delete(x,1)
Tableau d'élement périodique en chimie et le type map comme exemple dans un programme. 10 élément chimique indexe leur symbole. Print sur la clé "Li" qui affiche sa valeur Lithium
package main
import "fmt"
func main() {
elements := make(map[string]string)
elements["H"] = "Hydrogen"
elements["He"] = "Helium"
elements["Li"] = "Lithium"
elements["Be"] = "Beryllium"
elements["B"] = "Boron"
elements["C"] = "Carbon"
elements["N"] = "Nitrogen"
elements["O"] = "Oxygen"
elements["F"] = "Fluorine"
elements["Ne"] = "Neon"
fmt.Println(elements["C"])
}
On peut raccourcir et simplifier le code de cet manière sur type map
elements := map[string]string{
"H": "Hydrogen",
}
Cela permet d'une manière général de stocker de l'information. Ajoutons à la paire de clé et valeur une autre entrée tels que quelques terres rares
func main() {
LREE := map[string]map[string]string{
"Y": map[string]string{
"Nom":"Yttrium",
"Numéro et masse atomique":"N°39 et ma 88.90",
},
"Nd": map[string]string{
"Nom":"Neodyme",
"Numéro et masse atomique":"N°60 et ma 144.24",
},
"Eu": map[string]string{
"Nom":"Europium",
"Numéro et masse atomique":"N°63 et ma 151.25",
},
"Dy": map[string]string{
"Nom":"Dysprosium",
"Numéro et masse atomique":"N°66 et ma 162.50",
},
}
if el, ok := LREE["Y"]; ok {
fmt.Println(el["Nom"], el["Numéro et masse atomique"])
}
}
Le if est utilisé pour indéxer une recherche dans le tableau à travers une valeur de clé et print ces valeurs: test d'existence
Note sur la complexité la recherche dans map (dictionnaire) en fonction nombre de valeur à indéxer admettent un temps de performance des algorithmes. Ainsi dans la recherche dichotomique, l'algorithme en Θ(log(n)) ou logarithmique est à préviligié dans l'écriture de boucle de fonction.
Développement selon John Zelle
Analysez le problème Comprenez le problème à résoudre et sa nature. Essayez d’en apprendre le plus possible sur lui. Vous ne pourrez pas le résoudre avant de le connaître parfaitement.
Spécifiez le programme Décrivez très exactement ce que votre programme va faire. Vous ne devez pas déjà vous soucier de la façon dont il va le faire, mais plutôt décider très exactement ce qu’il va faire. Pour des programmes simples, cela consistera à décrire ce que seront les entrées et les sorties du programme et ce qui les relie.
Concevez le programme Formulez la structure globale du programme. C’est à ce moment que vous traiterez de la façon dont le programme résoudra le problème. Le travail principal est de concevoir le ou les algorithmes qui rempliront les tâches préalablement spécifiées.
Codez Traduisez les algorithmes conçus dans un langage de programmation et entrez les sur un ordinateur. Dans ces notes, nous programmerons nos algorithmes en Go.
Testez/Débuggez le programme Essayez votre programme et voyez s’il fonctionne comme vous le souhaitiez. S’il y a des erreurs (souvent appelées bugs), revenez à l’étape précédente et corrigez les. L’activité qui consiste à débusquer et corriger les erreurs s’appelle le debuggage. Durant cette phase, votre objectif est de trouver des erreurs, et pour cela, vous devez tester tout ce qui vous passe par le tête et qui pourrait planter le programme. Il sera utile de garder à l’esprit la maxime :
Nothing is foolproof because fools are too ingenious
Faites de la maintenance Continuez à développer votre programme en répondant aux besoins des utilisateurs. La plupart des programmes ne sont jamais vraiment finis ; ils continuent d’évoluer au fil des années d’utilisation.
Q1. On va partir sur un programme simple mathématique sur les puissances pour
introduire la saisie utilisateur. L'idée du programme est de connaitre une valeur
de la fonction inverse 1/r²
ou r est défini par l'utilisateur en début de
programme. Pour plus d'information sur la fonction 'input'
godoc fmt
Rechercher func scanf et utilisation de l'opérateur
&
pour trouver adresse de variable
A1. Solution au problème
package main
import "fmt"
func main() {
fmt.Print("Entrée un nombre: ")
var r float64
fmt.Scanf("%f", &r) // %f précision pour virgule flottante
x := 1 / ( r * r )
fmt.Println("Valeur inverse² :", x)
}
Q2. Un nouveau problème de poser et les chaines de caractère. Admettons une
énigme cryptographique sous un langage inconnu de symbole. On ne sait pas ce que
cela veut dire, mais on aimerait bien compter le nombre de caractère de la chaine.
Et en addition compter le nombre d'Octet de la string
via module "utf8" voir
godoc unicode/utf8
chaine à analyser: ⢎𝚿⣟ ⣹𝚲⢪ ⢱𝚵⢧
A2. Solution pour compter des runes et des octets. En cherchant dans godoc et
utf8 on trouve une fonction qui permet de compter les runes func RuneCount(p []byte)int
Pour convertir string vers octets
str := "hello"
b := []byte(str)
On ce retrouve donc avec ce programme runes_count.go
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "⢎𝚿⣟ ⣹𝚲⢪ ⢱𝚵⢧"
fmt.Printf("String %s\nLength: %d, Runes: %d\n", str,
len([]byte(str)), utf8.RuneCount([]byte(str)))
}
Valeurs affichées
String ⢎𝚿⣟ ⣹𝚲⢪ ⢱𝚵⢧
Length: 32, Runes: 11
Quelques explications sur le formatage de texte de la fonction printf
et
l'opérateur %
- suivi d'un “d”, il implique la présence d'un entier et le formate en décimal,
- suivi d'un “s”, il implique la présence d'une chaîne,
- suivi d'un “x”, il implique la présence d'un entier et le formate en hexadécimal,
- suivi d'un “g”, il implique la présence d'un réel,
- suivi d'un “v”, il formate de manière automatique n'importe quel type,
- suivi d'un “T”, il affiche le type de la valeur fournie
\n
renvoi à la ligne
D'ou
- %s renvoi à
str
- premier %d renvoi à
len([]byte(str))
- second %d renvoi à
utf8.RuneCount([]byte(str))
Q3. Enfin de compte les symboles cryptés on peut y insérer d'autre chaine de
caractère à l'intérieur. Remplacer dans la chaine ⢎𝚿⣟ ⣹ ⢱𝚵⢧ ⣾𝛀⣀ ⣒𝚹⢕
à la
position 10, 3 runes par ⣹𝚲⢪
A3. runes-copy.go
package main
import "fmt"
func main() {
s := "⢎𝚿⣟ ⣹ ⢱𝚵⢧ ⣾𝛀⣀ ⣒𝚹⢕"
r := []rune(s)
copy(r[10:10+3], []rune("⣹𝚲⢪"))
fmt.Printf("Script symbole original : %s\n", s);
fmt.Printf("Modification symbolique : %s\n", string(r))
}
Q4. La personne faisant de la rétro-ingénierie sur cette série de symbole codé
a remarqué qu'il était possible d'obtenir quelque chose de différent en inversant
le string
. Après tout cela dépend du sens que nous écrivons que nous faisons
la compréhension d'un langage! Alors inversé la chaine ⢎𝚿⣟ ⣹ ⢱𝚵⢧ ⣹𝚲⢪ ⣒𝚹⢕
A4. programme reverse.go
package main
import "fmt"
func main() {
s := "⢎𝚿⣟ ⣹ ⢱𝚵⢧ ⣹𝚲⢪ ⣒𝚹⢕"
a := []rune(s) // Une conversion
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i] // Assignement parallele
}
fmt.Printf("%s\n", string(a)) // Retour converti
}
Une fonction est une section indépendante de code qui maps zéro ou plusieurs
paramètres d'entrées INPUT
. Les fonctions sont également appelés procédures ou
des subroutines. Les fonctions sont représenté par une boite noire ou un code
est exécuté par la fonction. Noire car par exemple une voiture est une boite
noire pour son utilisateur: Il appuie sur l'accélérateur INPUT
et en sortie le
véhicule ce déplace OUTPUT
. Ce ne veut pas dire que vous savez comment marche
le moteur et tout le processus mécanique derrière. D'ou concept de boite noire.
Les notions de boite noire et boite blanche sont très importante pour résoudre des niveaux d'abtraction et d'isolation de système, utilisé définir architecture ou encore en notation UML
Sur nos programmes Go nous utilisons seulement auparavant une fonction principale
func main() {}
Schéma: ---------------
INPUT | | OUTPUT
----- > | Boite noire | ----- >
| |
---------------
// Déclaration de fonction:
type mytype int
func (p mytype) funcname(q int) (r,s int) { return 0,0 }
- func le mot-clé est utilisé pour déclarer une fonction;
- (p mytype) possible de donnée un type specifique appelé receiver dont celui-ci défini la méthode;
- funcname est le nom de votre fonction;
- (q int) est la variable d'entrée et copié dans la fonction pass-by-value;
- (r, s int) sont les paramètres de variable
return
; - { return 0,0 } corps de la fonction entre accolade;
Reprenons l'exemple du calcul de moyenne un peu auparavant pour construire une
fonction à partir du main()
func main() {
xs := []float64{98,93,77,82,83}
total := 0.0
for _, v := range xs {
total += v
}
fmt.Println(total / float64(len(xs)))
}
La moyenne average
d'une série de nombre est un problème classique dans l'
algorithminique. Nous allons appeler la fonction average
avec un slice en
*float64 ainsi qu'un return
en float64. On insert le code avant le main()
func average(xs []float64) float64 {
panic("Introuvable")
}
Cela est la signature de la fonction, incluant le paramètre (une liste de nombre)
qui est nommé xs et le type return
. Le built-in panic
appel un erreur
runtime, permet de casser le process, routine de la fonction pour debugger plus
simplement.
Ajoutons maintenant l'algorithme dans la fonction
func average(xs []float64) float64 {
total := 0.0
for _, v := range xs {
total += v
}
return total / float64(len(xs))
}
et modification sur le main()
, la variable nommé xs pas obligatoire
sous même nom.
func main() {
xs := []float64{98,93,77,82,83}
fmt.Println(average(xs))
}
Le paradigme de la programmation fonctionnelle en informatique est important dans la manière d'aborder les itérateurs. On utilise la fonction factorielle de manière récursive, elle suffit à elle même en tant que fonction pour s'appeler.
Factoriel, symbole !
n!= | 1 si n=0
| n(n - 1)! si n >= 1
Code go
package main
import "fmt"
func factorial(x uint) uint {
if x == 0 { // cas de base
return 1
} // cas récursif
return x * factorial(x-1)
}
func main() {
fmt.Println("Valeur vaut: ", factorial(5)) // calcul 5!
}
Dans cette fonction, la valeur de n! n'est pas connue tant que la condition
terminale (ici x == 0
) n'est pas atteinte. Le programme empile les appels
récursifs jusqu'à atteindre la condition terminale puis dépile les valeurs.
Exemple factorial(2)
- Si
x == 0
? Non. (x
vaut 2) - Trouver le factoriel de
x – 1
- Si
x == 0
? Non. (x
vaut 1) - Trouver le factoriel de
x – 1
* Six == 0
? Oui,return 1
. - return
1 * 1
- return
2 * 1
Il est possible de créer des fonctions à l'intérieur de fonctions
func main() {
x := 0
increment := func() int {
x++
return x
}
fmt.Println(increment())
fmt.Println(increment())
}
increment
ajoute 1 à la variable x
défini main()
Scope. Cet variable est
accessible via fonction increment
. Variable est non-local et appelé closure
d'ou increment
et x
forme closure
package main
var a = 6
func main() {
p()
q()
p()
}
func p() {
println(a)
}
func q() {
a := 9
println(a)
}
Entre les fonctions et le main()
les variables sont local ou global.
a := 5
est déclaré dans q()
et la valeur Print vaudra : 696
Go utilise le variable return
multiple pour une fonction tels que
func f() (int, int) {
return 5, 6
}
func main() {
x, y := f()
}
Cette notation est utilisé pour connaitre une valeur erreur à coté d'un résultat ou un booléen fonctionnant.
x, err := f()
x, ok := f()
Go a un état particulier entraine l'éxecution différée d'une commande à la sortie
d'une fonction courante. Exemple après avoir ouvert fichier defer
fermeture
f, _ := os.Open(filename)
defer f.Close()
defer
est particulièrement utilisé pour libérer des ressources, que cela soit de
la mémoire ou des processus.
Lorsque une erreur survient, durant appel de fonction, il vaut mieux indiqué
l'erreur par un code return
. Mais aussi irréversible qui entraine fin du
programme, on appel la fonction panic()
et récupéré l'exception lancé via
panic avec recover()
couplé à defer
package main
import "fmt"
func main() {
defer func() {
str := recover()
fmt.Println(str)
}()
panic("PANIC")
}
Les fonctions variadic
en utilisant ...
avant le type du dernier paramètre
indique prend zéro ou plus paramètres. Tels que Zéro ou plus ints
.
func myfunc(arg ...int) {}
func add(args ...int) int {
total := 0
for _, v := range args {
total += v
}
return total
}
func main() {
fmt.Println(add(1,2,3))
}
Q5. Ecrire une fonction qui calcul moyenne average d'un float64
slice et switch
A5. Code Go average.go
func average(xs []float64) (avg float64) {
sum := 0.0
switch len(xs) {
case 0:
avg = 0
default:
for _, v := range xs {
sum += v
}
avg = sum / float64(len(xs))
}
return
}
Q6. Exercice sur l'incrémentation. On veut afficher les dix premiers nombres de 1 à
10 et connaitre si ceux-ci sont soit paire ou impaire. Utiliser %
modulo
A6. Correction. On retrouve deux structures controle for
et if/else
et
l'incrémentation n+1 ce fait sur la ligne de condition par variable ++
package main
import "fmt"
func main() {
for i := 1; i <= 10; i++ {
if i % 2 == 0 {
fmt.Println(i, "paire")
} else {
fmt.Println(i, "impair")
}
}
}
Q7. La suite de Fibonacci commence tels que 1,1,2,3,5,8,13...
ou en notation
mathématique:
| F0 = 0
| F1 = 1
| Fn = Fn−1 + Fn−2 si n > 1
Ecrire une fonction prenant le type int
donnant plusieurs termes de la suite
de Fibonacci.
A7. fi.go
/*
Fibonacci sequence
*/
package main
import "fmt"
func fibonacci(value int) []int { // variable "value" func, return slice
x := make([]int, value) // Init Slice en int et len(value)
x[0], x[1] = 0, 1
for n := 2; n < value; n++ {
x[n] = x[n-1] + x[n-2]
}
return x
}
func main() {
for _, term := range fibonacci(11) { // Itérateur range de Fi(11)
fmt.Printf("%v ", term)
}
}
En informatique la notion de stack empile
revient régulièrement, web server,
algo.
i | k |<- [i++] --- push(k)
| l |pop() -- [i--]-> l
0 | m |
Schéma stack LIFO 'last in, first out'
Stack de la figure réprésenté par : [0:m] [1:l] [2:k]
Q8. Construire une stack LIFO avec un nombre fixe int. Définir push pour mettre quelques chose sur la stack et pop pour enlever de la fonction stack
A8. En premier nous définissons un nouveau type représente stack. Avec une
array
(gestion clé) et un index. 10 élements dans la stack
type stack struc { // struc permet de construire des types avancés
i int
data [10]int
}
Maintenant nous devons créer les fonctions push et pop. En premier défini la solution fausse d'une solution de push et problème pointer.
func (s stack) push(k int)
if s.i+1 > 9 {
return
}
s.data[s.i] = k
s.i++
}
La fonction fonctionne variable s
de type stack
. En utilisant appel
s.push(50)
on empile un entier 50 sur a stack. Mais la fonction push a
une copie de s
et ne fonctionnera pas. Rien n'est empilé sur la stack:
var s stack // s comme stack variable
s.push(25)
fmt.Printf("stack %v\n", s);
s.push(14)
fmt.Printf("stack %v\n", s);
---
prints:
stack [0:0]
stack [0:0]
Pour résoudre cela on donne à la fonction push un pointer de la stack d'ou:
func (s stack)push(k int) → func (s *stack)push(k int)
Pour la création du pointer on fait une allocation avec new()
tels que:
s := new(stack)
On ce retrouve avec le programme stack.go
:
/*
Stack LIFO
*/
package main
import "fmt"
// Déclare un nouveau type stack, 10 éléments
type stack struct {
i int
data [10]int
}
// Fonction push sur une stack
func (s *stack) push(k int) {
s.data[s.i] = k
s.i++
}
// Fonction pop enlève une stack
func (s *stack) pop() int {
s.i--
return s.data[s.i]
}
// Déclaration dans main() variabe s comme stack et push valeur
func main() {
var s stack
s.push(25)
s.push(14)
fmt.Printf("stack %v\n", s)
}
Une introduction au algoritme cryptographique et MD5. Retro-ingénierie sur un morceau de code d'une fonction.
/* MD5Hash
*/
package main
import (
"crypto/md5"
"fmt"
)
func main() {
hash := md5.New()
bytes := []byte("kenavo\n")
hash.Write(bytes)
hashValue := hash.Sum(nil)
hashSize := hash.Size()
for n := 0; n < hashSize; n += 4 {
var val uint32
val = uint32(hashValue[n])<<24 +
uint32(hashValue[n+1])<<16 +
uint32(hashValue[n+2])<<8 +
uint32(hashValue[n+3])
fmt.Printf("%x ", val)
}
fmt.Println()
}
Q9. Soit ce morceau de code source, comprendre son fonctionnement et définir les différentes étapes en sachant que le print vaut :
22917d0 d14dbfd0 ad38bb8b e1678dc4
Non trié | 0 | 3 | 12 | -1 | -5 | 5 |
Trié | -5 | -1 | 0 | 3 | 5 | 12 |
Il fonctionne en répètant des étapes qu'une liste doit être trié, compare chaque
paire d'item adjacent et le tri s'il est au mauvais endroit, passe l'étape s'il
n'a pas besoin de trier l'item. La fonction s'appelle bubblesort
/*
Algorithme formel
-----------------
procedure bubbleSort( A : list of sortable items )
do
swapped = false
for each i in 1 to length(A) - 1 inclusive do:
if A[i-1] > A[i] then
swap( A[i-1], A[i] )
swapped = true
end if
end for
while swapped
end procedure
*/
Q.10 Ecrire la fonction de l'algorithme quadratique complexité en Θ(n ² ) de tri bubblesort
A.10 code bubblesort.go
func main() {
n := []int{5, -1, 0, 12, 3, 5}
fmt.Printf("unsorted %v\n", n)
bubblesort(n)
fmt.Printf("sorted %v\n", n)
}
func bubblesort(n []int) {
for i := 0; i < len(n) - 1; i++ {
for j := i + 1; j < len(n); j++ {
if n[j] < n[i] {
n[i], n[j] = n[j], n[i]
}
}
}
}
Tri par sélection
Une première idée est de procéder ainsi :
- rechercher le plus petit, le mettre au début,
- rechercher le second plus petit, le mettre en deuxième position
- ...
Q.11 Ecrire une fonction qui prend un nombre variable int
et print chacun
d'entre eux sur une ligne séparée.
A.11 On utilise la syntaxe ...
qui prend un nombre d'argument au hasard
package main
import "fmt"
func main() {
prtthem(1, 4, 5, 7, 4)
prtthem(1, 2, 4)
}
func prtthem(numbers... int) { // numbers : slice of ints
for _, d := range numbers {
fmt.Printf("%d\n", d)
}
}
Définition d'un pointeur
Les pointeurs sont un type important en langage Go. Il s'utilise comme l'adresse pointant une valeur. On peut retrouver la valeur et la modifier avec un pointeur.
Un pointeur est déclaré en faisant précéder le type de donnée pointée par une
étoile*
(ie: *int // pointeur entier
). On prend l'adresse pointant sur une
donnée en la faisant précédé opérateur &
:
var i = 5
var j *int = &i // pointeur sur i
Exemple sur ce programme, pointeur référence & mémorise:
Sans pointeur
func zero(x int) {
x = 0
}
func main() {
x := 5
zero(x)
fmt.Println(x) // x vaut 5
}
Avec pointeur
func zero(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
zero(&x)
fmt.Println(x) // x vaut 0
}
L'opérateur &
référence à l'adresse du pointeur ainsi il est possible de
savoir l'adresse mémoire du pointeur tels que ce code:
var i int // Declare variable entier i
p = &i // Faire p pointer vers i
fmt.Printf("%v", p)
----
Print 0x7ff96b81c000a
Le mot clé nil représente un pointeur qui ne pointe sur aucune valeur licite. Cela peut représenter la valeur initiale d'un pointeur qui ne pointe vers rien NULL
func one(xPtr *int) {
*xPtr = 1
}
func main() {
xPtr := new(int)
one(xPtr)
fmt.Println(*xPtr) // x is 1
}
new
prend un argument, alloue de la mémoire pour la valeur du type et retourne
au pointeur. Les pointeurs sont utilisé en particulier avec struct
le type
structuré.
New()
alloues
make()
initialises utilisé seulement pour slices, maps, canaux
Exemple
var p *[]int = new([]int)
var v []int = make([]int, 100)
Retenez
v := make([]int, 100)
La définition d'un constructeur est une méthode particulière d'initialisation.
Pour plus d'information liser la doc package container/ring
et la fonction
new
défini un constructeur en Go.
Exemple avec le package os
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0} // Create a new File
return &f // Return the address of f
}
L'idéal serait de construire un programme seulement avec builtin
type Go,
ce qui arrive pas toujours avec des besoins spécifique de création.
Considérer cet exemple intéragi avec des formes:
package main
import ("fmt"; "math")
func distance(x1, y1, x2, y2 float64) float64 {
a := x2 - x1
b := y2 - y1
return math.Sqrt(a*a + b*b)
}
func rectangleArea(x1, y1, x2, y2 float64) float64 {
l := distance(x1, y1, x1, y2)
w := distance(x1, y1, x2, y1)
return l * w
}
func circleArea(x, y, r float64) float64 {
return math.Pi * r*r
}
func main() {
var rx1, ry1 float64 = 0, 0
var rx2, ry2 float64 = 10, 10
var cx, cy, cr float64 = 0, 0, 5
fmt.Println(rectangleArea(rx1, ry1, rx2, ry2))
fmt.Println(circleArea(cx, cy, cr))
}
Une façon rapide d'optimiser le programme est d'utiliser struct
et ces
paramètres fields pour définir Circle
type Circle struct {
x, y, r float64
}
Initialisation
Créer instance type Circle
var c Circle
Il est possible également d'utiliser la fonction new()
pour struct
c := new(Circle)
Cela alloue mémoire pour les fields mise à zéro et un retour pointeur et ainsi redonner une valeur fields tels que:
c := Circle{x: 0, y: 0, r: 5}
Fields
On peut accéder aux fields en utilisant l'opérateur .
fmt.Println(c.x, c.y, c.r)
c.x = 10
c.y = 5
Modifiant function CircleArea
avec Circle
func circleArea(c *Circle) float64 {
return math.Pi * c.r*c.r
}
Dans le main()
c := Circle{0, 0, 5}
fmt.Println(circleArea(&c))
Méthodes
On peut encore écrire la fonction d'une autre manière en utilisant une fonction
particulière nommée méthode , dans ce morceau de code on ajoute un receiver
permettant d'appeler une fonction utilisant l'opérateur .
func (c *Circle) area() float64 {
return math.Pi * c.r*c.r
}
Dans le print
fmt.Println(c.area())
On remarque que l'utilisation des pointeurs en Go en utilisant une méthode à
changé. En Go sait automatiquement ajouter un pointeur pour Circle
pour cette
méthode défini. Change rectangle:
type Rectangle struct {
x1, y1, x2, y2 float64
}
func (r *Rectangle) area() float64 {
l := distance(r.x1, r.y1, r.x1, r.y2)
w := distance(r.x1, r.y1, r.x2, r.y1)
return l * w
}
Et dans le Main()
r := Rectangle{0, 0, 10, 10}
fmt.Println(r.area())
Q.12 . Suppose type structuré est défini:
type Person struc {
name string
age int
}
Quel est différence entre ces deux lignes?
var p1 Person
p2 := new(Person)
Quel est différence entre ces deux allocations?
func Set(t *T) {
x = t
}
and
func Set(t T) {
x= &t
}
func Map(f func(int) int, l []int) []int {
j := make([]int, len(l))
for k, v := range l {
j[k] = f(v)
}
return j
}
func main() {
m := []int{1, 3, 4}
f := func(i int) int {
return i * i
}
fmt.Printf("%v", (Map(f, m)))
}
Une map()-function
est une fonction attaché à une liste et une fonction tels
que:
map(f(),(a1, a2, . . . , an−1, an)) = (f(a1), f(a2), . . . , f(an−1), f(an))
Le code ci-dessus est la solution à l'équation pour le type int
Q.13 Ré-écrire la map-fonction avec une interface générique, en typé int, str
A.13 code map-function-gen.go
package main
import "fmt"
//* define the empty interface as a type
type e interface{}
func mult2(f e) e {
switch f.(type) {
case int:
return f.(int) * 2
case string:
return f.(string) + f.(string) + f.(string)
+ f.(string)
}
return f
}
func Map(n []e, f func(e) e) []e {
m := make([]e, len(n))
for k, v := range n {
m[k] = f(v)
}
return m
}
func main() {
m := []e{1, 2, 3, 4}
s := []e{"a", "b", "c", "d"}
mf := Map(m, mult2)
sf := Map(s, mult2)
fmt.Printf("%v\n", mf)
fmt.Printf("%v\n", sf)
}
Une interface est un ensemble de méthodes, c'est à dire des fonctions qu'il est
possible d'implémenter pour une type défini. Ce morceau de code défini struct
type s
avec un field
qui défini 2 méthodes pour s
type S struct { i int }
func (p *S) Get() int { return p.i }
func (p *S) Put(v int) { p.i = v }
En définissant type interface
I
de plusieurs méthodes
type I interface {
Get() int
Put(int)
}
Rappeler vous les méthodes communes pour Rectangle
& Circle
ont area
. En
définissant une interface nommé Shape
:
type Shape interface {
area() float64
}
L'interface implémenté en langage Go est une forme de duck typing, loin du pure duck typing car la compliation Go va checké statique le type implémenté niveau interface. La conversion checké au moment runtime. Interface est l'idée dans d'autres langages comme pure abstract virtual base classes en C++, typerclasses en Haskell ou duck typing en python.
Aucun langage combine comme en Go : Valeur interface, statique type checking, dynamique runtime conversion, et pas nécessité déclaré qu'un type satisfait une interface.
“Parallelisme est a propos de la performance;Concurrent est à propos du design de programme. Rob Pike”
Quand on code de gros programme informatique il est nécessaire plusieurs sous-programme. L'exemple du web server dans l'attente réponse du navigateur et renvoi à une page HTML en réponse. Chaque requète est comme un sous-programme.
Il serait idéal pour de sous-programme comme ceux-ci de pouvoir les lancer
en même temps en parallèle (surtout multiple requète server et de page web)
Lancer du multi-tache simultanément s'appel des tâches concurrent. Go est
riche dans l'utilisation concurrent avec goroutines
et canaux
Une goroutine
est une fonction capable de lancer en concurrent d'autres
fonctions. Le mot clé pour utiliser une goroutine
est go
puis l'invocation
de fonction.
ready("Green Tea", 2) // Appel fonction normal
go ready("Green Tea", 2) ← ready() lancé comme goroutine
package main
import "fmt"
func f(n int) {
for i := 0; i < 10; i++ {
fmt.Println(n, ":", i)
}
}
func main() {
go f(0) // goroutine de f(0)
var input string
fmt.Scanln(&input) // permet de tout print avant fin de programme et sortir
}
Ce programme utilise deux goroutines l'une implicite le main()
et la seconde
à l'appel de go f(0)
Utilisons la fonction time.Sleep
pour donner un peu de delay execution tache
package main
import (
"fmt"
"time"
)
func ready(w string, sec int) {
time.Sleep(time.Duration(sec) * time.Second)
fmt.Println(w, "is ready!")
}
func main() {
go ready("Tea", 2)
go ready("Coffee", 1)
fmt.Println("I'm waiting")
time.Sleep(5 * time.Second)
}
Dans le même style, il est possible d'éxecuter un temps aléatoire avec la fonction
rand
import "math/rand"
amt := time.Duration(rand.Intn(250)) // sur un entier
Avant de sortir du programme on attend 5 seconde, l'exemple auparavant on
demande à l'utilisateur un scanln
. On peut fixer le problème en utilisant des
canaux
Un canal est comparable à un pipeline en UNIX shell. Le type est spécifique au
canal. Donc un canal chan
permet à deux goroutines go
de communiquer
en eux et de synchroniser leur execution. Créer canal avec make
ci := make(chan int) // ci integer
cs := make(chan string) // cs stringe
cf := make(chan interface{}) // cf interface
Pour recevoir et envoyer à un canal on utilise l'opérateur <-
ci <- 1 // Envoyer integer 1 vers canal ci
<-ci // Recevoir un integer du canal ci
i := <-ci // Recevoir du canal ci et le stocker dans i
Fermer un channel
x, ok = <-ch
var c chan int //déclare c variable entier canal
func ready(w string, sec int) {
time.Sleep(time.Duration(sec) * time.Second)
fmt.Println(w, "is ready!")
c <- 1 // Envoi integer 1
}
func main() {
c = make(chan int) // init c
go ready("Tea", 2) // lancer goroutine
go ready("Coffee", 1)
fmt.Println("I'm waiting, but not too long")
<-c // Attendre de recevoir du canal 'sans valeur'
<-c // Deux goroutines donc deux canaux associer valeur.
}
L'appel select
fonctionne comme un switch
mais dans l'utilisation d'un
canal.
Modification du programme précédent avec select
:
var c chan int
func ready(w string, sec int) {
time.Sleep(time.Duration(sec) * time.Second)
fmt.Println(w, "is ready!")
c <- 1 // Envoi integer 1
}
func main() {
c = make(chan int)
go ready("Tea", 2)
go ready("Coffee", 1)
fmt.Println("I'm waiting, but not too long")
L: for {
select {
case <-c:
i++
if i > 1 {
break L
}
}
}
}
Le langage Go utilise goroutine
comme concurrent, pour améliorer le parallelisme
utiliser la fonction runtime.GOMAXPROCS(n)
qui paramètre le nombre maximum
de CPU's processeurs utiliser pour lancer simultanément.
Parlonds rapidement de la communication avec le monde externe. Tels que fichiers, dossiers, réseaux ou éxecuter d'autre programme. GO's I/O avec les interfaces:
io.Reader et io.Writer
Lire à partir d'un fichier
package main
import "os"
func main() {
buf := make([]byte, 1024)
f, _ := os.Open("/etc/passwd") // Ouvrier le fichier
defer f.Close() // sur de fermer le fichier
for {
n, _ := f.Read(buf) // Lecture 1024 octets à la fois
if n == 0 { break } // fin fichier
os.Stdout.Write(buf[:n]) // écrire contenu vers os.Stdout
}
}
Version buffered
package main
import ( "os"; "bufio")
func main() {
buf := make([]byte, 1024)
f, _ := os.Open("/etc/passwd")
defer f.Close()
r := bufio.NewReader(f)
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
for {
n, _ := r.Read(buf)
if n == 0 { break }
w.Write(buf[0:n])
}
}
Le package os/exec
a une fonction de lancer des commandes externes. Exemple
en lançant la commande UNIX ls -l
affichant chaque fichier,
information, droit d'accès
Comparaison script shell UNIX et Go création dossier inexistant
Version UNIX Shell
------------------
if [ ! -e name ]; then
mkdir name
else
# error
fi
Version Go
----------
if f, e := os.Stat("name"); e !=
nil {
os.Mkdir("name", 0755)
} else {
// error
}
- fmt
- io
- io/ioutil
- bufio
- sort
- strconv
- strings
- math/rand
- os
- sync
- flag
- time
- sync/atomic
- encoding/json
- encoding/gob
- html/template
- net/http
- net/rpc
- unsafe
- runtime
- reflect
- os/exec
- path/filepath
- errors
- container/list
- hash
- crypto/aes
- crypto/md5
- web toolkit http://www.gorillatoolkit.org/
- Site statique http://gohugo.io/overview/quickstart/ + https://github.com/drone/jkl
- sqlite https://godoc.org/code.google.com/p/go-sqlite/go1/sqlite3
- driver mongoDB noSQL https://labix.org/mgo
- Golang-EN book PDF https://github.com/golang/go/wiki/Books
- Pratique de programmation en Go https://code.google.com/p/golang-france/downloads/list
- Learn https://github.com/gyuho/learn#contents
svp comment on fait pour Inclure un fichier dans un autre avec le GoLang ?