Golang : bases
Paquets
<syntaxhighlight lang="go"> package main
import ( "fmt" "math/rand" )
func main() { fmt.Println("My favorite number is", rand.Intn(10)) } </syntaxhighlight>
Les programmes sont composés de paquets; ils commencent l'éxecution dans le paquet main. Le programme ci-dessus utilise fmt et math/rand; par convention, le nom du paquet est le même que le dernier élément du chemin d'importation. Par exemple, le paquet "math/rand" comprend les fichiers qui commencent avec la déclaration du paquet rand.
imports
Une bonne pratique consiste à factoriser les imports:
<syntaxhighlight lang="go"> import ( "fmt" "math" ) </syntaxhighlight>
Identifiants exportés
Un élément est exporté d'un paquet si il commence par une majuscule (en python, je peux faire random.randint et randint sera exporté; en Go, il faudra faire math.Pi pour accéder à l'identifiant "pi").
Fonctions
Une fonction peut prendre 0 ou plusieurs arguments; la déclaration se fait en *nom_variable* type_variable :
<syntaxhighlight lang="go"> package main
import "fmt"
func add(x int, y int) int { return x + y }
func main() { fmt.Println(add(42, 13)) }
//En détail //func est le mot clef des fonctions //Les variables sont renvoyées par valeur *dans le cas présent, en tout cas* //Si plusieurs retours, utiliser des parenthèses
func nom_fonction(entrée1 type, entrée2 type) type_retour {
opérations return valeur
}
//Quand on déclare plusieurs variables du même type, on peut ne mettre le type qu'à la fin : func add(x, y int) int { return x + y }
//Exemple de retour multiple func swap(x, y string) (string, string) { return y, x }
func main() { a, b := swap("hello", "world") fmt.Println(a, b) }
//On peut renvoyer les valeurs comme étant nommées //Ce qui est encouragé dans les fonctions de plus de quelques lignes //Je ne sais pas si c'est une référence ou simplement une nouvelle assignation func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
func main() { fmt.Println(split(17)) }
</syntaxhighlight>
Variables
L'instruction var déclare une liste de variables, comme dans les listes d'arguments de fonction, le type est en dernier.
Une instruction var peut être au niveau du paquet ou au niveau de la fonction. On voit les deux dans cet exemple.
<syntaxhighlight lang="go"> package main
import "fmt"
var c, python, java bool
func main() { var i int fmt.Println(i, c, python, java) } </syntaxhighlight>
Initialisation
On peut initialiser les variables directement (une initialisation par variable). Si c'est le cas, préciser le type est facultatif:
<syntaxhighlight lang="go">
var i, j int = 1, 2
func main() { var c, python, java = true, false, "no!" fmt.Println(i, j, c, python, java) } </syntaxhighlight>
Initialisation courte
- Dans une fonction*, on peut initialiser une variable de façon courte avec l'opérateur :=
<syntaxhighlight lang="go"> func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!"
fmt.Println(i, j, k, c, python, java) } </syntaxhighlight>
Types de base
Les types de base, proches du C, sont:
bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // alias pour uint8 rune // alias pour int32 // représente un "code point" Unicode float32 float64 complex64 complex128
La différence entre int et unsigned int:
- Un int Peut stocker des négatifs.
- Un uint ne peut stocker que des positifs, mais ils peuvent être deux fois plus gros.
En 32 bits:
int: –2147483648 to 2147483647 uint: 0 to 4294967295
Un exemple auquel je ne comprend pas tout:
<syntaxhighlight lang="go"> package main
import ( "fmt" "math/cmplx" )
var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i) )
func main() { const f = "%T(%v)\n" fmt.Printf(f, ToBe, ToBe) fmt.Printf(f, MaxInt, MaxInt) fmt.Printf(f, z, z) } </syntaxhighlight>
Valeur 0
Une variable déclarée mais pas initialisée reçoit une valeur dite 0:
- 0 pour les types numériques
- False pour un bool
- "" Pour un str
Conversions de types
L'expression T(v) convertit la valeur v au type T.
Quelques conversions numériques :
var i int = 42 var f float64 = float64(i) var u uint = uint(f)
Ou, plus simplement :
i := 42 f := float64(i) u := uint(f)
Contrairement au C, en Go l'affectation entre les éléments de type différent exige une conversion explicite.
<syntaxhighlight lang="go">
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y))
var z uint = uint(f)
fmt.Println(x, y, z)
}
</syntaxhighlight>
Inférence de type
Lors de la déclaration d'une variable sans préciser un type explicite (soit en utilisant la syntaxe := ou var =), le type de la variable est déduit de la valeur sur le côté droit.
Lorsque le côté droit de la déclaration est typé, la nouvelle variable est de ce même type :
var i int j := i // j est un entier
Mais quand le côté droit contient une constante numérique non typée, la nouvelle variable peut être un int , float64, ou complex128 en fonction de la précision de la constante :
i := 42 // int f := 3.142 // float64 g := 0.867 + 0.5i // complex128
Constantes
Les constantes sont déclarées comme des variables, mais avec le mot-clé const.
Les constantes peuvent être un caractère, une chaîne, un booléen, ou des valeurs numériques.
Les constantes ne peuvent pas être déclarées en utilisant la syntaxe := .
<syntaxhighlight lang="go">
package main
import "fmt"
const Pi = 3.14
func main() { const World = "世界" fmt.Println("Hello", World) fmt.Println("Happy", Pi, "Day")
const Truth = true fmt.Println("Go rules?", Truth) } </syntaxhighlight>
Constantes numériques
Les constantes numériques sont des valeurs de haute précision. Une constante non typée prend le type requis par son contexte. (Un int peut stocker au maximum un entier de 64 bits et parfois moins.)
Pas tout compris...
<syntaxhighlight lang="go> package main
import "fmt"
const ( Big = 1 << 100 Small = Big >> 99 )
func needInt(x int) int { return x*10 + 1 } func needFloat(x float64) float64 { return x * 0.1 }
func main() { fmt.Println(needInt(Small)) fmt.Println(needFloat(Small)) fmt.Println(needFloat(Big)) } </syntaxhighlight>
Boucles
For
Go n'as qu'une seule structure de boucle : for. Le principe est sensiblement le même qu'en Python, Bash, Powershell...
On a trois éléments avec des ; à la fin des deux premiers:
- Une initialisation au format court : i := 0;
- Tant que : i < 10 ;
- Une action sur la variable : i++
Suivis des actions entre {}
Ces trois déclarations ne sont pas entre parenthèses, mais les accolades sont obligatoires.
<source lang="go">
func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) } </source>
For continu et absence de while
On peut se passer de l'initialisation et de l'action sur la variable:
<source lang="go"> package main
import "fmt"
func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) } </source>
On voit ici que le langage Go n'as pas de boucle while ! Le code ci-dessous fait la même chose. On peut remarquer qu'on a même pas de besoin de ;
<source> package main
import "fmt"
func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) } </source>
Boucle infinie
Il suffit de ne pas donner de condition à la bouicle pour qu'elle ne s'arrête jamais :
<source lang="go"> package main
func main() { for { } } </source>
Conditions
if
La condition if suit le même principe que la boucle for, de façon assez simple : <source lang="go"> package main
import ( "fmt" "math" )
func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) }
func main() { fmt.Println(sqrt(2), sqrt(-4)) } </source>
On peut commencer un if par une déclaration à exécuter avant le début de la condition. Il est aussi à noter que les variables déclarées dans une condition n'ont de portée que dans celle-ci.
<source lang="go"> package main
import ( "fmt" "math" )
func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim }
func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } </source>
if / else
On peut faire un else, qui a lui aussi accès aux variables déclarées dans la condition en elle-même.
<source lang="go"> package main
import ( "fmt" "math" )
func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // can't use v here, though return lim }
func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) } </source>
Switch
Un switch est une façon plus simple de faire des if/else (un peu comme le case en bash). Le switch s'arrête au cas sélectionné, on a pas besoin d'avoir un *break* comme en PHP, par exemple.
<source lang="Go"> func main() { fmt.Print("Le chiffre est ") switch x := 22; x { case 20: fmt.Println("Vingt") case 22: fmt.Println("vingt-deux") default: fmt.Println(x) } } </source>
Ici, on a la valeur de la valeur os qui est évaluée, avec des cas spéciaux pour les valeurs "darwin" et "linux" et un cas par défaut pour le reste du temps. Les valeurs sont évaluées de haut en bas et on s'arrête au premier case trouvé.
switch sans condition
Un switch sans condition est le même qu'un switch true. C'est uen façon simple d'écrire de longs if-then-else:
<source lang="go"> func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } } </source>
Defer
Le mot clef defer signifie que l'instruction associée ne sera exécutée qu'à la fin de la fonction dans laquelle on se trouve. L'appel est évalué immédiatement, mais exécuté plus tard.
<source lang="go"> func main() { defer fmt.Println("world")
fmt.Println("hello") } </source>
Les defer peuvent être accumulés. Dans ce cas, ils sont mis dans une pile du type LIFO.
<source lang="go"> func main() { fmt.Println("counting")
for i := 0; i < 10; i++ { defer fmt.Println(i) }
fmt.Println("done") } </source>
Pointeurs
Go utilise des pointeurs, qui sont comme des liens symboliques : il contiennent l'adresse mémoire de la valeur à laquelle ils se réfèrent. Un pointeur vers a s'écrit *a; sa valeur 0 est nil.
Lire un input en console
Reader.ReadString
https://golang.org/pkg/bufio/#Reader.ReadString
Lire un input se fait avec la lib bufio et son objet ReadString
<source lang="go"> func main() { reader := bufio.NewReader(os.Stdin) fmt.Println("And puppies ?") puppies, _ := reader.ReadString('\n') fmt.Println(puppies, "will come and lick your face !") } </source>
Reader lit jusqu'à ce que l'utilisateur rentre un caractère de délimitation ("\n" ici) et renvoie le texte délimiteur compris dans puppies; si il rencontre une erreur, il renverra le etxte jusqu'à l'erreur dans puppies et renverra l'erreur dans _.
Scanner
Dans le cas d'un input utilisateur, scanner est beaucoup plus simple à utiliser :
<source lang="go"> package main
// Exercice de test
import(
"fmt" //"math/rand" "bufio" "os" )
func main() {
//Reading with a scanner fmt.Println("How many puppies ?") scanner := bufio.NewScanner(os.Stdin) scanner.Scan() fmt.Println(scanner.Text(), "will come and lick your face")
}
</source>
Commentaires
Les commentaires monoligne s'écrivent avec //:
<source lang="Go"> func main() {
// Print “Hello, World!” to console fmt.Println("Hello, World!")
} </source>
Les commentaires doivent avoir l'indentation du code correspondant
Les commentaires en bloc s'écrivent comme en c:
<source lang="Go"> /* Everything here will be considered a block comment
- /
</source>