// Fonction qui ne retourne rien
func doSomething() {
// Do something...
}

// Fonction qui prend un paramètre et qui ne retourne rien
// Notez que le nom de la variable et son type sont inversés par rapport aux langages plus classiques
func doSomethingWithThat(foo string) {
// Do something...
}

// Fonction qui prend plusieurs paramètres et qui retourne un paramètre
// Notez que foo et bar sont tous les deux des strings
// Notez également que le type retourné par la fonction est indiqué juste avant d’ouvrir l’accolade
func computeSomething(foo, bar string, baz int) int {
 // Do something…
 return 42
}

// Fonction qui retourne plusieurs paramètres (oui oui c’est possible)
func returnsALotOfThings() (int, int, string, int, float64) {
	return 24, 24, “toto”, 42, 42.42
}

var strSalut string = “salut”
strSalutations := “salutations”

var thisIsTrue bool = true
thisIsFalse := false

// Par défaut, le type de number sera de int
number := 42

// Par défaut, le type de numberFloat sera un float64
numberFloat := 42.42

// Les slices sont instanciés à l’aide du mot clé make
var sliceOfInt []int
sliceOfInt = make([]int, 0) // sliceOfInt est maintenant un slice de int de taille 0

// Pour ajouter des éléments à un slice, on utilise le mot clé append
sliceOfInt = append(sliceOfInt, 12, 24, 42) 
sliceOfInt[0] == 12
sliceOfInt[1] == 24
sliceOfInt[2] == 42
sliceOfInt[3] // crash du programme puisque le tableau ne contient que 3 éléments

// Exemple de map:
m := map[string]int{
	“reponse”: 42,
	“question”: 21,
}

reponse := m[“reponse”] // reponse == 42
question := m[“question”] // question == 21

// Définition d’un objet Animal

type Animal struct {
	Nom string
	Pattes int
}

func main() {
// Déclaration de variables de type Animal :
var chien Animal // chien est de type Animal
chien.Nom = “Rex”

var chat *Animal // chat a la valeur nil par défaut car c’est  un pointeur non instancié
chat.Nom = “Tom” // crash du programme car chat == nil

// Instanciation d’un objet *Animal avec le mot clé new
var poisson *Animal = new(Animal)
poisson.Nom = “Bubble”

// Instanciation d’un object *Animal avec l’inférence de type (ma méthode préférée)
souris := &Animal{
		Nom: “Jerry”,
	Pattes: 4,
}
// Cela marche aussi avec une structure qui n’est pas un pointeur :
cheval := Animal{
		Nom: “Jolly”,
	Pattes: 4,
}

func main() {
	// Déclaration d’un tableau de int de 5 éléments :
	arrayInt := []int{1, 2, 3, 4, 5}

	// Les conditions n’ont pas de parenthèses en Go
	if arrayInt [1] == 2 {
		
} else if arrayInt [3] != 2 {

} else {

}
// Déclaration d’un tableau de strings
arrayString := []string{“salut”, “monsieur”}

// On peut switch sur des strings :
switch arrayString[0] {
case “salut”:
// Le break est implicite en Go
case “monsieur”:
	// Si on veut exécuter le bloc suivant, il faut utiliser le mot-clé fallthrough
	fallthrough
case “madame”:
	
}

// Tout comme les conditions, les boucles n’ont pas de parenthèses :

for i := 0; i < 10; i++ [
	
}

// On peut boucler sur un array :

for index, valeur := range arrayString {
	fmt.Println(index, valeur)
}
// Dans l’ordre, ça affichera “0 salut” et “1 monsieur”
}

// Définition d’un objet Animal
type Animal struct {
	Nom    string
	Pattes int
}

// Définition d’une méthode Crier pour le type Animal :
func (animal *Animal) Crier() string {
	return "AHHHHH!"
}

// Définition d’un objet Chien qui hérite d’Animal :
type Chien struct {
	Animal
}

// Surcharge de la méthode Crier pour le type Chien :
func (chien *Chien) Crier() string {
	return "OUAF!"
}

func main() {
	// Instantiation d’un pointeur sur Chien
	chien := &Chien{
		Animal{
			Nom:    "Rex",
			Pattes: 4,
		},
	}
	chien.Crier()        // retourne “OUAF!”
	chien.Animal.Crier() // retourne “AHHHHH!”
}

package main
import (
   "procons/models"
   "fmt"
)

func produit(queueBanane chan *models.Banane) {
   producteur := models.Producteur{}
   for {
         // On fabrique une banane
         banane := producteur.FabriqueBanane()
         fmt.Println(“Banane produite !”)
         // Cette syntaxe un peu bizarre signifie qu’on ajoute une banane dans le channel
         queueBanane <- banane
   }
}

func consomme(queueBanane chan *models.Banane) {
   consommateur := &models.Consommateur{}
   // On boucle sur le channel, à chaque fois qu’une banane sera ajoutée au channel, nous réaliserons un tour de boucle
   for banane := range queueBanane {
       consommateur.Mange(banane)
       if consommateur.BananesMangées == 5 {
           fmt.Println("J'ai fini de manger !")
           return
       }
   }
}

func main() {
   // On instancie un channel de banane
   queueBanane := make(chan *models.Banane)

   // On lance dans un processus à part la production de bananes.
   go produit(queueBanane)
   consomme(queueBanane)
}

package main

import (
  "fmt"
  "procons/models"
)

func produit(queueBanane chan *models.Banane, queueNourriture chan bool) {
  producteur := models.Producteur{}
  for {
     banane := producteur.FabriqueBanane()
     fmt.Println("Banane produite !")
     queueBanane <- banane
       // On attend que le consommateur finisse de manger pour continuer la boucle.
     <- queueNourriture
  }
}

func consomme(queueBanane chan *models.Banane, queueNourriture chan bool) {
  consommateur := &models.Consommateur{}
  for banane := range queueBanane {
     consommateur.Mange(banane)
       // On notifie le producteur qu'on a fini de manger.
     queueNourriture <- true
     if consommateur.BananesMangées == 5 {
        fmt.Println("J'ai fini de manger !")
        return
     }
  }
}
func main() {
  queueBanane := make(chan *models.Banane)
  // Dans ce cas, on se fiche du type du channel.
  queueNourriture := make(chan bool)
  go produit(queueBanane, queueNourriture)
  consomme(queueBanane, queueNourriture)
}