Extragerea părților dintr-o colecție
Uneori nu ai nevoie de un slice întreg. Ai nevoie doar de o parte din el: un interval de elemente, primele sau ultimele câteva, bucăți de aceeași mărime sau o fereastră mobilă de vecini.
Alte limbaje au metode pentru liste, cum ar fi slice, take, drop, chunked și windowed. Go nu are aceste metode. În schimb, Go are o expresie de slice: s[low:high] dă elementele de la indexul low până la indexul high, fără a-l include pe acesta. Omite low ca să începi de la început sau omite high ca să mergi până la sfârșit.
O expresie de slice nu copiază nimic. Slice-ul nou împarte memoria cu cel original. Dacă schimbi un element din parte, se schimbă și originalul. Când ai nevoie de o copie independentă, folosește slices.Clone() din pachetul slices.
Pentru lucrurile pe care o expresie de slice nu le poate face singură, această lecție folosește o buclă for, pachetul slices sau o mică funcție helper generică.
Majoritatea exemplelor de pe această pagină folosesc aceleași date: cuvintele one two three four five six.
Sintaxa expresiei de slice
Cele trei forme cel mai des întâlnite. n este un număr de elemente sau un index. Indexul de sfârșit nu este niciodată inclus.
s[low:high] // elements from index low up to, but not including, high
s[:n] // the first n elements
s[n:] // everything from index n to the end
Feliere (slice)
Felierea înseamnă a lua elementele aflate pe anumite poziții alese.
Pentru o serie de poziții vecine, folosește o expresie de slice: words[1:4] dă elementele de la indexurile 1, 2 și 3. Ține minte că indexul de sfârșit este exclus: ca să obții elementele de la indexurile 1 până la 3 scrii 1:4.
O expresie de slice nu poate sări peste elemente. Ca să iei fiecare al doilea element sau elementele de pe poziții alese de tine, folosește o buclă for și adaugă cu append() fiecare element ales într-un slice nou.
Felierea unui interval
words[1:4] începe la indexul 1 și se oprește înainte de indexul 4. Rezultatul are 3 elemente.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
// indexurile 1, 2 și 3 - indexul de sfârșit 4 nu este inclus
part := words[1:4]
fmt.Println(part)
}
Feliere cu pas
Ca să iei fiecare al doilea element, o buclă for cu i += 2 trece prin indexurile 0, 2 și 4 și adună cuvintele.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
// ia fiecare al doilea element, de la indexul 0 până la indexul 4
var part []string
for i := 0; i <= 4; i += 2 {
part = append(part, words[i])
}
fmt.Println(part)
}
Feliere după o listă de indici
Pozițiile 3, 5 și 0 sunt păstrate într-un slice de int. Bucla alege cuvântul de pe fiecare poziție, așa că rezultatul iese în ordinea indicilor.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
indices := []int{3, 5, 0}
// alege elementele de pe pozițiile date, în acea ordine
var part []string
for _, i := range indices {
part = append(part, words[i])
}
fmt.Println(part)
}
Take și Drop
Take înseamnă să păstrezi un anumit număr de elemente de la un capăt. Drop înseamnă să arunci un anumit număr de elemente de la un capăt și să păstrezi restul.
Amândouă sunt expresii de slice în Go. words[:3] ia primele 3 elemente, iar words[len(words)-3:] ia ultimele 3. words[1:] elimină primul element, iar words[:len(words)-5] elimină ultimele 5.
Există și variante condiționale: takeWhile, takeLastWhile, dropWhile și dropLastWhile. Acestea nu numără elementele. Ele merg de la un capăt și se opresc la primul element care nu trece de condiție. Go nu are variante încorporate, așa că o mică funcție helper generică face treaba: găsește indexul la care condiția nu mai este adevărată și returnează o expresie de slice de la acel index.
Ia primele sau ultimele n elemente
words[:3] păstrează primele 3 cuvinte. words[len(words)-3:] păstrează ultimele 3.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
first3 := words[:3] // primele 3 elemente
last3 := words[len(words)-3:] // ultimele 3 elemente
fmt.Println(first3)
fmt.Println(last3)
}
Elimină primele sau ultimele n elemente
words[1:] sare peste primul cuvânt. words[:len(words)-5] sare peste ultimele 5 cuvinte, așa că rămâne doar one.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
withoutFirst := words[1:] // elimină primul element
withoutLast5 := words[:len(words)-5] // elimină ultimele 5 elemente
fmt.Println(withoutFirst)
fmt.Println(withoutLast5)
}
Ia cât timp o condiție este adevărată
TakeWhile păstrează cuvintele de la început până când întâlnește un cuvânt care începe cu f. TakeLastWhile păstrează cuvintele de la sfârșit până când întâlnește three. strings.HasPrefix() verifică începutul unui șir.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
// TakeWhile păstrează elementele de la început cât timp keep() spune da, apoi se oprește.
func TakeWhile[T any](items []T, keep func(T) bool) []T {
for i, v := range items {
if !keep(v) {
return items[:i]
}
}
return items
}
// TakeLastWhile păstrează elementele de la sfârșit cât timp keep() spune da, apoi se oprește.
func TakeLastWhile[T any](items []T, keep func(T) bool) []T {
for i := len(items) - 1; i >= 0; i-- {
if !keep(items[i]) {
return items[i+1:]
}
}
return items
}
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
fromStart := TakeWhile(words, func(s string) bool { return !strings.HasPrefix(s, "f") })
fromEnd := TakeLastWhile(words, func(s string) bool { return s != "three" })
fmt.Println(fromStart)
fmt.Println(fromEnd)
}
Elimină cât timp o condiție este adevărată
DropWhile sare peste cuvintele de 3 litere de la început și păstrează tot de la three încolo. DropLastWhile sare peste cuvintele de la sfârșit cât timp acestea conțin litera i. strings.Contains() verifică dacă un șir conține o bucată de text.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
// DropWhile sare peste elementele de la început cât timp skip() spune da și păstrează restul.
func DropWhile[T any](items []T, skip func(T) bool) []T {
for i, v := range items {
if !skip(v) {
return items[i:]
}
}
return nil
}
// DropLastWhile sare peste elementele de la sfârșit cât timp skip() spune da și păstrează restul.
func DropLastWhile[T any](items []T, skip func(T) bool) []T {
for i := len(items) - 1; i >= 0; i-- {
if !skip(items[i]) {
return items[:i+1]
}
}
return nil
}
func main() {
words := []string{"one", "two", "three", "four", "five", "six"}
fromStart := DropWhile(words, func(s string) bool { return len(s) == 3 })
fromEnd := DropLastWhile(words, func(s string) bool { return strings.Contains(s, "i") })
fmt.Println(fromStart)
fmt.Println(fromEnd)
}
Chunked (bucăți)
Împărțirea în bucăți înseamnă tăierea unui slice în bucăți de aceeași mărime. Ultima bucată poate fi mai scurtă atunci când elementele nu se împart exact.
Go are asta încorporat începând cu versiunea 1.23: slices.Chunk(s, n). Nu returnează direct un slice de slice-uri. Returnează un iterator, care îți dă bucățile una câte una într-o buclă for range. Folosește slices.Collect() ca să aduni toate bucățile într-un [][]int.
Ca să lucrezi imediat pe fiecare bucată, parcurge bucățile și fă treaba în interiorul buclei.
Împărțire în bucăți de câte 3
Numerele de la 0 la 13 sunt tăiate în bucăți de câte 3. Ultima bucată are doar 2 elemente.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
)
func main() {
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
// slices.Chunk dă bucățile una câte una; slices.Collect le adună într-un slice de slice-uri
chunks := slices.Collect(slices.Chunk(numbers, 3))
fmt.Println(chunks)
}
Suma fiecărei bucăți
Aceleași bucăți, dar fiecare este adunată în interiorul buclei. Se păstrează doar sumele.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
)
func main() {
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
// adună fiecare bucată de 3 imediat ce este produsă
var sums []int
for chunk := range slices.Chunk(numbers, 3) {
sum := 0
for _, n := range chunk {
sum += n
}
sums = append(sums, sum)
}
fmt.Println(sums)
}
Windowed (ferestre)
O fereastră este un grup de elemente vecine. Parcurgerea cu ferestre mută grupul de-a lungul slice-ului și adună fiecare poziție. Spre deosebire de bucăți, ferestrele se suprapun.
Go nu are windowed încorporat. O buclă for cu o expresie de slice items[i:i+size] face asta. O mică funcție helper generică Windowed o face reutilizabilă. Cu un step, fereastra se mută cu mai multe poziții odată. Cu partial setat pe true, o fereastră mai scurtă de la sfârșit este păstrată în loc să fie aruncată.
O fereastră de 2 care se mută cu 1 pas este același lucru cu împerecherea fiecărui element cu următorul. În Go este o buclă care se oprește cu un element înainte de sfârșit.
Ferestre de câte 3
Fereastra are 3 cuvinte și se mută cu 1 poziție de fiecare dată. Bucla se oprește când o fereastră întreagă nu mai încape.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// Windowed returnează fiecare grup de size elemente, mutându-se cu o poziție de fiecare dată.
func Windowed[T any](items []T, size int) [][]T {
var result [][]T
for i := 0; i+size <= len(items); i++ {
result = append(result, items[i:i+size])
}
return result
}
func main() {
numbers := []string{"one", "two", "three", "four", "five"}
fmt.Println(Windowed(numbers, 3))
}
Ferestre cu pas și ferestre parțiale
Fereastra are 3 numere și se mută cu 2 poziții de fiecare dată. Pentru că partial este true, ultima fereastră [9 10] este păstrată chiar dacă este scurtă.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// Windowed returnează grupuri de size elemente, mutându-se cu step poziții de fiecare dată.
// Cu partial = true, este păstrat și un grup mai scurt de la sfârșit.
func Windowed[T any](items []T, size, step int, partial bool) [][]T {
var result [][]T
for i := 0; i < len(items); i += step {
end := i + size
if end > len(items) {
if !partial {
break
}
end = len(items)
}
result = append(result, items[i:end])
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(Windowed(numbers, 3, 2, true))
}
Suma fiecărei ferestre
Fiecare fereastră de 3 vecini este adunată. Se păstrează doar sumele.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
numbers := []int{1, 2, 3, 4, 5}
// adună fiecare fereastră de 3 vecini
var sums []int
for i := 0; i+3 <= len(numbers); i++ {
window := numbers[i : i+3]
sum := 0
for _, n := range window {
sum += n
}
sums = append(sums, sum)
}
fmt.Println(sums)
}
Împerecherea fiecărui element cu următorul
Prima buclă construiește un Pair din fiecare cuvânt și vecinul său. A doua buclă compară aceiași vecini și notează dacă primul cuvânt este mai lung decât al doilea.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// Pair conține două elemente vecine.
type Pair struct {
A string
B string
}
func main() {
letters := []string{"one", "two", "three", "four", "five"}
// fiecare element împreună cu cel de după el
var pairs []Pair
for i := 0; i+1 < len(letters); i++ {
pairs = append(pairs, Pair{letters[i], letters[i+1]})
}
fmt.Println(pairs)
// aceeași parcurgere, dar comparând lungimile vecinilor
var longer []bool
for i := 0; i+1 < len(letters); i++ {
longer = append(longer, len(letters[i]) > len(letters[i+1]))
}
fmt.Println(longer)
}