I primi programmi in GO (Goland) - Università Statale di Milano
Ricordo che per eseguire da terminale i programmi con il linguaggio di programmazione GO bisogna:
- Installare ovviamente il compilatore GO
link per scaricare il compilatore: https://golang.org/doc/install
- entrare nel prompt di Win 7-8-10 del vostro computer
- uscire dal proprio profilo digitando con il comando: cd\
- digitare successivamente: cd go e dare invio
- digitare successivamente: cd bin e dare invio
a questo punto siete pronti per mandare in esecuzione il programma che avrete scritto con un editor salvando con estensione go.
La sintassi per eseguire il programma è il seguente:
go run (nome programma)
ecco l'esempio in foto
// Il nostro primo programma scriverà il messaggio
// "Ma che bella giornata!" nel terminale
package main
import "fmt"
func main() {
fmt.Println("Ma che bella giornata!")
}
- Installare ovviamente il compilatore GO
link per scaricare il compilatore: https://golang.org/doc/install
- entrare nel prompt di Win 7-8-10 del vostro computer
- uscire dal proprio profilo digitando con il comando: cd\
- digitare successivamente: cd go e dare invio
- digitare successivamente: cd bin e dare invio
a questo punto siete pronti per mandare in esecuzione il programma che avrete scritto con un editor salvando con estensione go.
La sintassi per eseguire il programma è il seguente:
go run (nome programma)
ecco l'esempio in foto
// Il nostro primo programma scriverà il messaggio
// "Ma che bella giornata!" nel terminale
package main
import "fmt"
func main() {
fmt.Println("Ma che bella giornata!")
}
--------------------------------------------------------------------------------------------------------
// In Go è possibile utilizzare valori di svariati tipi
// fra i quali anche string, integer, boolean
// float, etc. Vediamo insieme qualche esempio
// basilare su come usare questi tipi.
package main
import "fmt"
func main() {
// String, che possono essere concatenate con `+`.
fmt.Println("go" + "lang")
// Integer e float.
fmt.Println("1+1 =", 1+1)
fmt.Println("7.0/3.0 =", 7.0/3.0)
// Boolean, con i classici operatori booleani
// AND, OR e NOT.
fmt.Println(true && false)
fmt.Println(true || false)
fmt.Println(!true)
}
--------------------------------------------------------------------------------------------------------------
// In Go, le _variabli_ sono dichiarate esplicitamente e
// sono usate dal compilatore, ad esempio, per contrillare
// la correttezza dei tipi di valori nelle invocazioni
// delle funzioni.
package main
import "fmt"
func main() {
// `var` dichiara una o più variabili.
var a string = "initial"
fmt.Println(a)
// Puoi dichiarare più variabili in un colpo solo.
var b, c int = 1, 2
fmt.Println(b, c)
// Go dedurrà il tipo delle variabili inizializzate.
var d = true
fmt.Println(d)
// Variabili dichiarate senza una inizializzazione
// corrispondente sono _zero-valued_. Ad esempio, lo
// zero-value di un `int` è `0`.
var e int
fmt.Println(e)
// La sintassi `:=` è una abbreviazione per dichiarare
// ed inizializzare una variabile, in questo caso è
// l'abbreviazione di `var f string = "short"`.
f := "short"
fmt.Println(f)
}
-----------------------------------------------------------------------------------------------------
// Go ammette l'utilizzo di _costanti_ di tipo string, boolean,
// e di tipo numerico
package main
import "fmt"
import "math"
// La keyword `const` viene utilizzata per dichiarare una costante
const s string = "constant"
func main() {
fmt.Println(s)
// La keyword `const` può essere utilizzata ovunuque
// la keyword `var` è ammessa
const n = 500000000
// Le espressioni costanti vengono calcolate in aritmetica
// a precisione arbitraria
const d = 3e20 / n
fmt.Println(d)
// Una costante numerica non ha un tipo fin quando non gli
// viene assegnato esplicitamente, ad esempio tramite un cast.
fmt.Println(int64(d))
// Per assegnare un tipo ad una costante di tipo numerico
// si può anche utilizzare la constante in un contesto che richiede
// un tipo, quali un assegnamento od una chiamata di funzione.
// In questo caso `math.Sin` si aspetta un valore di tipo `float64`.
fmt.Println(math.Sin(n))
}
--------------------------------------------------------------------------------------------------
esempio di somma di tre numeri
package main
import (
"fmt"
)
// somma di tre numeri interi
func main() {
var x int
var y int
var q int
var z int
fmt.Print("inserisci x \n")
fmt.Scanf("%d\n", &x)
fmt.Print("inserisci y \n")
fmt.Scanf("%d\n", &y)
fmt.Print("inserisci q \n")
fmt.Scanf("%d\n", &q)
z=x+y+q
fmt.Printf("il risultato della somma... %d \n", z)
}
-------------------------------------------------------------------------------------------------------------
// `for` è l'unico costrutto per eseguire cicli
// in Go. Qui vengono presentati tre tipi di cicli
// `for`.
package main
import "fmt"
func main() {
// Il ciclo più semplice, con una singola condizione.
// (simile al while degli altri linguaggi)
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
// Un classico ciclo `for` inizializzazione/test/incremento.
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
// Un `for` senza condizioni si ripeterà sempre finché
// non esci dal ciclo con un `break` oppure fai
// un `return` per la funzione che lo racchiude.
for {
fmt.Println("loop")
break
}
}
-----------------------------------------------------------------------------------------
// Modificare il flusso di controllo con `if` ed `else` in Go
// è semplice e ricalca la classica sintassi vista in altri linguaggi.
package main
import "fmt"
func main() {
// Questo è un esempio base
if 7%2 == 0 {
fmt.Println("7 è pari")
} else {
fmt.Println("7 è dispari")
}
// È possibile avere un comando `if` senza il ramo `else`
if 8%4 == 0 {
fmt.Println("8 è divisibile per 4")
}
// Un comando può precedere il test del comando `if`.
// Qualsiasi variabile dichiarata in questo comando
// è visibile all'interno di tutti i rami del comando `if`
if num := 9; num < 0 {
fmt.Println(num, "è negativo")
} else if num < 10 {
fmt.Println(num, "ha una cifra")
} else {
fmt.Println(num, "ha più di una cifra")
}
}
// Nota che non sono necessarie le parentesi intorno alle condizioni
// del comando `if` in Go, ma le parentesi graffe sono necessarie.
-------------------------------------------------------------------------------------
// Gli _switch_ esprimono condizionali attraverso più
// rami.
package main
import "fmt"
import "time"
func main() {
// Ecco uno switch semplice.
i := 2
fmt.Print(i, " in lettere è ")
switch i {
case 1:
fmt.Println("uno")
case 2:
fmt.Println("due")
case 3:
fmt.Println("tre")
}
// Puoi utilizzare le virgole per dividere più
// espressioni nella stessa dichiarazione `case`.
// In questo esempio utilizziamo anche il caso
// opzionale `default`, che viene eseguito nel
// caso l'espressione non possa essere valutata
// in nessuno dei rami precedenti.
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("siamo nel fine settimana")
default:
fmt.Println("oggi è un giorno feriale")
}
// Uno `switch` senza espressione è un metodo
// alternativo per esprimere la logica degli if/else.
// Qui vediamo anche come le espressioni dei `case`
// possono anche non essere costanti.
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("non è ancora passato mezzogiorno")
default:
fmt.Println("è passato mezzogiorno")
}
// Nel caso volessimo fare cose differenti per una
// variabile di cui non conosciamo il tipo
// (ad esempio, una variabile `interface{}` che vedremo
// più avanti), possiamo utilizzare un `type switch`.
// In questo caso, stiamo prima convertendo la variabile
// v in una `interface{}`, dopo stiamo usando `.(type)`
// che segnala di usare il type switch.
v := 3
switch interface{}(v).(type) {
case string:
fmt.Println("v è di tipo `string`")
case int:
fmt.Println("v è di tipo `int`")
default:
fmt.Println("v è di un altro tipo ancora")
}
}
--------------------------------------------------------------------------------------------------
//In Go, un array è una sequenza numerata di elementi
//con una lunghezza specifica
package main
import "fmt"
func main() {
//Qui creaiamo un array chiamato a che conterrà esattamente 5 elementi di tipo int.
//Il tipo degli elementi e la lunghezza sono entrambi parti integranti del tipo dell’array.
//Per default gli array vengono inizializzati allo zero value,
//che per gli elementi di tipo int significa essere inizializzati a 0.
var a [5]int
fmt.Println("emp:", a)
//Possiamo assegnare un valore ad uno specifico indice utilizzando
//la classica sintassi: array[indice] = valore ,
//e possiamo ottenere il valore con array[indice].
a[4] = 100
fmt.Println("set:", a)
fmt.Println("get:", a[4])
//La funzione builtin len restituisce la lunghezza dell’array.
fmt.Println("len:", len(a))
//Utilizza questa sintassi per dichiarare ed inizializzare un array nella stessa linea.
b := [5]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b)
//Gli array sono di base monodimensionali, ma possono essere composti
//per costruire strutture di dati multidimensionali
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
//Nota che gli array vengono visualizzati nella forma [v1 v2 v3 …] se stampati con la funzione fmt.Println.
-------------------------------------------------------------------------------
//Gli Slice sono un data type fondamentale di Go,
//e rendono la gestione degli array più semplice e potente.
package main
import "fmt"
func main() {
//A differenza degli array, gli slice vengono definiti
//dando soltanto il tipo degli elementi che contengono
//(non il numero di elementi). Per creare uno slice vuoto
//con una lunghezza diversa da 0, usa la funzione make.
//Di seguito creiamo uno slice di string di lunghezza 3 (all’inizio zero-valued).
s := make([]string, 3)
fmt.Println("emp:", s)
//Possiamo impostare e prendere valori esattamente come negli array.
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])
//len restituisce, come ci si potrebbe aspettare, la lunghezza dello slice.
fmt.Println("len:", len(s))
//Oltre a queste operazioni di base, gli slice ne hanno molte altre
//che permettono loro di essere più funzionali degli array.
//Una di queste è la funzione append, che restituisce uno slice contentente
//uno o più ulteriori valori. Nota che abbiamo bisogno di accettare il valore
//restituito da append, visto che potremmo ricevere uno slice completamente nuovo.
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println("apd:", s)
//Gli slice possono anche essere copiati con la funzione copy.
//Di seguito creiamo uno slice vuoto c della stessa lunghezza di s
//e copiamo i valori di s in c.
c := make([]string, len(s))
copy(c, s)
fmt.Println("cpy:", c)
//Gli slice supportano un operatore “slice” che ha la sintassi
//variabileSlice[inizio:fine]. Per esempio, di seguente generiamo
//uno slice degli elementi s[2], s[3] e s[4].
l := s[2:5]
fmt.Println("sl1:", l)
//Il seguente crea uno slice fino al quinto elemento (escludendo il quinto).
l = s[:5]
fmt.Println("sl2:", l)
//E il seguente lo crea degli elementi dopo il secondo elemento (includendo il secondo).
l = s[2:]
fmt.Println("sl3:", l)
//Possiamo, inoltre, dichiarare ed inizializzare uno slice in una sola riga.
t := []string{"g", "h", "i"}
fmt.Println("dcl:", t)
//Gli slice possono essere composti in strutture di dati a più dimensioni.
//Il numero degli elementi degli slice all’interno può variare,
//a differenza degli array multi-dimensionali.
biDim := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
biDim[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
biDim[i][j] = i + j
}
}
fmt.Println("2d: ", biDim)
}
// Nota che, anche se gli slice sono dei tipi diversi dagli array,
//anche essi possono essere stampati tramite fmt.Println.
---------------------------------------------------------------------------------------------------
// Le Map sono la struttura built-in di Go per gli Array associativi
//(in altri linguaggi si possono trovare strutture simili sotto
// il nome di hash table o dizionari).
package main
import "fmt"
func main() {
//Per creare una nuova map vuota, utilizza la funzione built-in make:
//make(map[tipo-chiave]tipo-valore).
m := make(map[string]int)
//Puoi impostare i valori della map utilizzando
//la sintassi tipica nomemap[chiave] = valore
m["k1"] = 7
m["k2"] = 13
//Passare la map ad una funzione di stampa (tipo Println)
//mostrerà tutte le coppie chiave-valore della map
fmt.Println("map:", m)
//Puoi ottenere il valore di una chiave con nomemap[chiave].
v1 := m["k1"]
fmt.Println("v1: ", v1)
//La funzione built-in len restituisce il numero
//di coppie chiave-valore se la si invoca su una map
fmt.Println("len:", len(m))
//La funzione built-in delete rimuove le coppie chiave-valore dalla map
delete(m, "k2")
fmt.Println("map:", m)
//Quando si accede ad una map è possibile controllare il secondo valore restituito opzionale
// che indica la presenza o meno di una chiave all’interno di una map.
//Questo parametro può essere utilizzato per discernere il caso in cui una chiave
//non è presente dal caso in cui una chiave ha assegnato lo zero-value (ad esempio 0 o "").
//In questo caso non abbiamo nemmeno bisogno del valore associato alla chiave,
//per cui scartiamo il primo parametro utilizzando l’identificatore blank _
_, prs := m["k2"]
fmt.Println("prs:", prs)
//È anche possibile dichiarare ed inizializzare una nuova map con la sintassi seguente
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
}
//Nota che le map vengono mostrate nel formato map[k:v k:v] se vengono stampate con fmt.Println.
---------------------------------------------------------------------------------
//I range statement permettono di iterare sugli elementi di una varietà di strutture di dati, similmente //ai foreach di altri linguaggi. Vediamo come usare range con alcune delle strutture di dati che //abbiamo già visto.
package main
import "fmt"
func main() {
//Di seguito utilizziamo un range per sommare i numeri di uno slice. (Con gli array viene fatto allo //stesso modo).
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("somma:", sum)
//range sugli array e sugli slice restituisce sia l’indice sia il valore di ognuno degli elementi. Prima //non avevamo bisogno di utilizzare l’indice, quindi l’avevamo ignorato utilizzando
// il blank identifier _. Qualche volta potremmo avere anche solo bisogno dell’indice,
//e ignorare il suo valore.
for i, num := range nums {
if num == 3 {
fmt.Println("indice:", i)
}
}
//range su una map itera sulle coppie chiave-valore.
kvs := map[string]string{"a": "alice", "b": "bob"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range sulle stringhe itera sui singoli codici Unicode della stringa. Il primo valore rappresenta //l’indice posizionale, il secondo valore rappresenta la rune Unicode in sè per sè.
for i, c := range "go" {
fmt.Println(i, c)
}
}
-----------------------------------------------------------------------------------------
// Le Funzioni svolgono un ruolo fondamentale in Go.
//Capiremo come usare le funzioni tramite una serie di esempi
package main
import "fmt"
//Questa è una funzione che accetta due parametri di tipo int
//e restituisce la loro somma (sempre di tipo int).
func plus(a int, b int) int {
//Go non restituirà il valore dell’ultima espressione:
//se bisogna restituire un valore,
//lo si deve restituire esplicitamente con il comando return
return a + b
}
//Nelle funzioni con parametri multipli dello stesso tipo si può omettere
//il tipo per i parametri consecutivi che hanno lo stesso tipo,
//e indicare il tipo solo per l’ultimo parametro.
func plusPlus(a, b, c int) int {
return a + b + c
}
func main() {
//Puoi chiamare una funzione con la classica sintassi
//nomefunzione(parametri).
res := plus(1, 2)
fmt.Println("1+2 =", res)
res = plusPlus(1, 2, 3)
fmt.Println("1+2+3 =", res)
}
-----------------------------------------------------------------------------------
//Go supporta valori restituiti multipli, similmente a python.
//Questa funzionalità è usata spesso nel Go idiomatico,
//per esempio per restituire sia il valore
//sia l’eventuale errore nell’esecuzione di una funzione.
package main
import "fmt"
//L’indicazione (int, int) in questa funzione
//ci dice che la funzione restituisce due int.
func vals() (int, int) {
return 3, 7
}
func main() {
//Di seguito utilizzando il multiple assignment creiamo
//due diverse variabili dai valori restituiti della funzione vals().
a, b := vals()
fmt.Println(a)
fmt.Println(b)
//Se vuoi soltanto avere una parte dei valori restituiti,
//usa il blank identifier _.
_, c := vals()
fmt.Println(c)
}
----------------------------------------------------
//Le funzioni variadiche possono essere chiamate
//con un numero arbitrario di parametri in coda. fmt.Println è il classico esempio
//di una funzione variadica.
package main
import "fmt"
//Questa è un esempio di funzione che accetta
// un numero arbitrario di parametri di tipo int.
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func main() {
//Le funzioni variadiche possono essere invocate
//nel classico modo indicando ogni parametro separatamente
sum(1, 2)
sum(1, 2, 3)
//Se i parametri si trovano dentro uno slice,
//puoi passarlo direttamente ad una funzione variadica
//tramite la sintassi seguente: nomefunzione(slice...)
nums := []int{1, 2, 3, 4}
sum(nums...)
}
-------------------------------------------------------------------------------------
//Go supporta le funzioni anonime, che possono formare delle chiusure.
// Le funzioni anonime sono utili quando
//vuoi definire una funzione senza darle un nome.
package main
import "fmt"
//Questa funzione intSeq restituisce un’altra funzione,
//che definiamo anonimamente dentro il corpo
//della funzione intSeq.
// La funzione restituita racchiude la variabile i per formare una chiusura.
func intSeq() func() int {
i := 0
return func() int {
i += 1
return i
}
}
func main() {
//Facciamo una chiamata ad intSeq, assegnando il risultato (una funzione) a nextInt.
//Il valore di questa funzione racchiude in sé stessa
//il valore di i, il quale verrà aggiornato la prossima volta che utilizziamo nextInt.
nextInt := intSeq()
//Osserviamo l’effetto della chiusura facendo una chiamata a nextInt un po’ di volte.
fmt.Println(nextInt())
fmt.Println(nextInt())
fmt.Println(nextInt())
//Per confermare che lo stato è unico a quella funzione particolare,
//creiamo e testiamone una nuova.
newInts := intSeq()
fmt.Println(newInts())
}
------------------------------------------------------------------------------------------
// Go supporta le Funzioni ricorsive
//Ecco il classico esempio di una funzione ricorsiva, il fattoriale.
package main
import "fmt"
//Questa funzione fact invoca se stessa
//fin quando non raggiunge il caso base per n uguale a 0.
func fact(n int) int {
if n == 0 {
return 1
}
return n * fact(n-1)
}
func main() {
fmt.Println(fact(7))
}
---------------------------------------------------------------------------------------
//Go permette l’utilizzo dei puntatori,
//che si traduce nell’abilità di passare
//riferimenti a valori all’interno del programma.
package main
import "fmt"
//Dimostreremo come i puntatori funzionino diversamente
// dai valori tramite 2 funzioni: zeroval e zeroptr.
//zeroval ha un parametro di tipo int,
//quindi il parametro passato sarà un valore,
//non un puntatore. Quando chiameremo la funzione zeroval,
//il suo parametro ival verrà copiato da quello della funzione chiamante.
func zeroval(ival int) {
ival = 0
}
//zeroptr invece ha un parametro di tipo *int,
//e ciò significa che è un puntatore a un int.
//L’istruzione *iptr nel corpo della funzione
//permette di dereferenziare l’indirizzo di memoria
//puntato da iptr in modo da otternere il suo valore.
//Se si assegna un valore ad *iptr si va
//a modificare il valore all’indirizzo di memoria puntanto.
func zeroptr(iptr *int) {
*iptr = 0
}
func main() {
i := 1
fmt.Println("iniziale: ", i)
zeroval(i)
fmt.Println("zeroval: ", i)
//La formula &i restituisce l’indirizzo nella memoria di i,
//ovvero un puntatore ad i.
zeroptr(&i)
fmt.Println("zeroptr: ", i)
//Anche i puntatori possono essere stampati.
fmt.Println("puntatore:", &i)
}
//zeroval non cambia il valore di i in main,
//zeroptr invece sì perché ha un riferimento
//al valore nella memoria di quell’indirizzo.
------------------------------------------------------------------------
//In Go le structs sono collezioni di field (campi)
//a cui è associato un tipo. Sono utili
//per raccogliere insieme dati in modo da formare dei record
package main
import "fmt"
//Questa struct person possiede due campi,
//rispettivamente name ed age.
type person struct {
name string
age int
}
func main() {
//Con questa sintassi si crea una nuova struct.
fmt.Println(person{"Nicola", 20})
//Puoi indicare il nome del campo quando crei una struct.
fmt.Println(person{name: "Luigi", age: 30})
//I field non indicati verrano inizializzati
//con il loro zero-value.
fmt.Println(person{name: "Alessandro"})
//Inserire un & a prefisso della dichiarazione
//permetterà di ottenere un puntatore alla struct
fmt.Println(&person{name: "Luca", age: 40})
//Puoi accedere ai campi della struct con l’operatore . (punto).
s := person{name: "Mario", age: 50}
fmt.Println(s.name)
//Puoi utilizzare il punto anche per i puntatori a struct.
//Il puntatore verrà dereferenziato automaticamente.
sp := &s
fmt.Println(sp.age)
//Le struct sono mutabili.
sp.age = 51
fmt.Println(sp.age)
}
----------------------------------------------------------------------------------------
Commenti
Posta un commento