Slice

Una slice è un involucro intorno ad un array. Possiede:

  • reference . riferimento all’elemento iniziale dell’array
    • default: primo elemento
  • length - dimensione della slice
    • default: fino alla fine dell’array
  • capacity - capacità o spazio preallocato per la slice
    • >= length
b := a[2, 5]

Da 2 incluso fino a 5 escluso.

Slice1

Mentre un array ha allocazione statica, una slice ha allocazione dinamica, cioè nello heap.

Le slice vengono usate, per esempio, come segue:

(090slices.go):

package main

import "fmt"

func main() {

	// Creazione di slice di tre elementi iniziali
	// Allocata in memoria dinamica - heap
	// Gli elementi sono zero di default
	s := make([]string, 3)
	fmt.Println("emp:", s)

	// Uso simile ad array
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("set:", s)
	fmt.Println("get:", s[2])

	// Funzione di lunghezza della slice
	fmt.Println("len:", len(s))

	// Estensione di una slice
	// Può silenziosamente riallocare memoria
	// pertanto è necessaria un'assegnazione alla
	// nuova locazione in memoria (s è un puntatore implicito)
	s = append(s, "d")
	s = append(s, "e", "f")
	fmt.Println("apd:", s)

	// Semplice assegnazione copia solo il puntatore
	// poichè sono nello heap la copia è per riferimento
	p := s
	// Cambiare una slice cambia anche l'altra
	p[1] = "x"
	fmt.Println("old: ", s)
	fmt.Println("new: ", p)

	// Copia di una slice
	// Crea nuova slice
	c := make([]string, len(s))
	// copia su destinazione di sorgente
	copy(c, s)
	// le due slice sono ora differenti
	c[1] = "y"
	fmt.Println("old: ", s)
	fmt.Println("copy: ", c)

	// Operatore di slice - fetta
	// Dal primo indicato incluso all'ultimo indicato escluso
	l := s[2:5]
	fmt.Println("sl1:", l)

	// Dal primo assoluto incluso all'ultimo indicato escluso
	l = s[:5]
	fmt.Println("sl2:", l)

	// Dal primo indicato incluso all'ultimo assoluto
	l = s[2:]
	fmt.Println("sl3:", l)

	// Dichiarazione e inizializzazione simultanea di slice
	// Se tra quadre non c'è dimensione è una slice
	// se c'è è un array
	t := []string{"g", "h", "i"}
	fmt.Println("dcl:", t)

	// Creazione di una slice vuota
	// Le graffe sono obbligatorie
	z := []string{}
	// Una variabile dichiarata deve sempre essere usata
	fmt.Println(">>", z, "<<")

	// Slice multidimensionale
	// Entrambe le dimensioni sono dinamiche:
	// puntatori a puntatori
	twoD := make([][]int, 3)
	for i := 0; i < 3; i++ {
		innerLen := i + 1
		twoD[i] = make([]int, innerLen)
		for j := 0; j < innerLen; j++ {
			twoD[i][j] = i + j
		}
	}
	fmt.Println("2d: ", twoD)
}

Una slice deve essere allocata in memoria dinamica, con l'operatore make, simile al new di C++ e Java:

s := make([]string, 3)

Occorre indicare il tipo degli elementi della slice e la dimensione iniziale dell'allocazione.

Una slice inizialmente è vuota, contiene elementi azzerati.

Si possono settare e accedere a elementi della slice iniziale come per un array,e la funzione len() conosce la dimensione della slice.

E' possibile estendere la slice iniziale con la funzione append() che prende almeno due argomenti: il nome della slice e gli elementi aggiunti alla fine.

Si può copiare una slice in un'altra slice che abbia almeno la dimensione della prima.

Si chiama slice perchè è possibile estrarre una fetta della slice originale in un'altra variabile, dichiarata e inizializzata dello stesso tipo. Nell'esempio:

a := s[2:5]

a contiene gli elementi di s a partire dall'elemento di indice 2 (il terzo) incluso e a terminare all'elemento di indice 5 (il sesto) escluso.

Se l'inizio della fetta - prima dei due punti - non è indicato, si intende il primo elemento incluso. Se la fine della fetta - dopo i due punti - non è indicato, si intende l'ultimo elemento incluso.

Si può avere una slice di slices. Per esempio con la dichiarazione:

twoD := make([][]int, 3)

si crea una slice di tre slices.

La dimensione esterna è di tre elementi (slices). la dimensione di ogni elemento interno (slice) non è specificata, e possono essere slice interne di dimensione differente.

Ogni slice interna deve a sua volta essere costruita con, p.es:

twoD[i] = make([]int, innerLen)

Si nota, per chi proviene dal C o C++, la presenza nascosta di puntatori a memoria allocata dinamicamente.

Esempio 1

(091slice1.go):

package main
import "fmt"
func main() {
  a := [...]int{0, 10, 20, 30, 40, 50, 60, 70} // array
  fmt.Println(len(a)) // 8
  fmt.Println(a)
  b := a[2:6] // da 2 a 6 escluso
  fmt.Println(len(b), cap(b)) // 4 6
  fmt.Println(b)
  fmt.Println(b[1]) // indicizza una slice, non un array
  c := a[5:] // da 5 alla fine
  fmt.Println(c)
  d := a[:5] // dall’inizio a 5 escluso
  fmt.Println(d)
  e := a[:] // la slice copre tutto a
  fmt.Println(e)
  f := a // array, non slice
  fmt.Println(f)
  g := d[:3] // slice di slice
  fmt.Println(g)
  h := []int{1, 2, 3, 4} // slice con array sottostante
  fmt.Println(h)
  fmt.Println(len(h), cap(h)) // 4 4 
}

Esempio 2

append aggiunge elementi alla fine della slice:

  • Length e capacity sono estese
  • Capacity può essere maggiore di length

(092slice.app.go):

package main
import "fmt"
func main() {
  sl1 := []string{"alpha", "bravo", "charlie"}
  fmt.Println("old slice : ", sl1)
  fmt.Println("old length : ", len(sl1))
  fmt.Println("old capacity : ", cap(sl1))
  sl1 = append(sl1, "delta")
  fmt.Println("new slice : ", sl1)
  fmt.Println("new length : ", len(sl1))
  fmt.Println("new capacity : ", cap(sl1))
}

Esempio 3

Svuotare una slice. Due modi:

[092slice-del.go):

package main
import "fmt"
func main() {
  a := []string{"A", "B", "C", "D", "E"}
  fmt.Println(a, len(a), cap(a)) // [A B C D E] 5 5
  a = nil // tootalmente ripulita
  fmt.Println(a, len(a), cap(a)) // [] 0 0
  b := []string{"A", "B", "C", "D", "E"}
  b = b[:0] // slice con length zero, ma ha ancora capacity
  fmt.Println(b, len(b), cap(b)) // [] 0 5
}

Esempio 4

Rimuovere un elemento. La slice deve essere ricostruita.

(094slice-rem.go):

package main
import "fmt"
func main() {
  strSlice := []string{"Canada", "Japan", "Germany", "Italy"}
  fmt.Println(strSlice)
  // append di più elementi
  strSlice = append(strSlice, "UK", "France", "Spain")
  fmt.Println(strSlice)
  // rimuove l’elemento con indice 2
  strSlice = append(strSlice[:2], strSlice[3:]...)
  fmt.Println(strSlice)
  // rimuove il primo elemento
  strSlice = strSlice[1:]
  fmt.Println(strSlice)
  // rimuove l’ultimo elemento
  strSlice = strSlice[:len(strSlice)-1]
  fmt.Println(strSlice)
}