Golang : bases

De Justine's wiki
Aller à la navigation Aller à la recherche

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>