Obținerea unui singur element
Această lecție arată cum obții un singur element dintr-un slice: după poziția lui, după o condiție sau la întâmplare. Arată și cum verifici dacă un element există.
Go nu are null. Când o căutare poate eșua, o funcție Go returnează două valori: elementul și un bool numit ok. Este același tipar value, ok pe care îl știi de la map-uri.
Citirea unei poziții care nu există, de exemplu words[5] într-un slice cu 5 elemente, oprește programul cu un panic (o eroare la rulare). De aceea verificăm întotdeauna mai întâi len().
Alte limbaje au multe funcții gata făcute pentru asta. Go păstrează limbajul mic: folosești paranteze pătrate, pachetul slices sau un mic helper pe care îl scrii o singură dată.
Obținerea după poziție
Fiecare element dintr-un slice are o poziție numită index. Primul index este 0, deci ultimul este len(words)-1.
Ca să citești un element după poziție, scrie indexul între paranteze pătrate: words[3]. Așa citește Go elementul de la o poziție, primul element sau ultimul element.
Go nu are elementAtOrNull() sau elementAtOrElse(). Un mic helper generic verifică indexul față de len() și returnează fie value, ok, fie rezultatul unei funcții de rezervă. Astfel, un index în afara intervalului nu poate provoca un panic.
Element de la o poziție
words[3] citește elementul de la indexul 3. Fiindcă numărătoarea începe de la 0, acesta este al patrulea cuvânt.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
words := []string{"one", "two", "three", "four", "five"}
// Pozițiile încep de la 0, deci indexul 3 este al patrulea element.
fmt.Println(words[3])
}
Primul și ultimul element
Primul element este words[0]. Ultimul este words[len(words)-1], pentru că lungimea este 5, dar ultimul index este 4.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
words := []string{"one", "two", "three", "four", "five"}
// Primul element este la indexul 0, ultimul la len - 1.
fmt.Println(words[0])
fmt.Println(words[len(words)-1])
}
Element de la o poziție, sau ok == false
ElementAt verifică indexul față de len(). Indexul 1 este în interval, deci returnează "two" și true. Indexul 5 este în afara intervalului, deci returnează valoarea zero "" și false în loc să provoace un panic.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// ElementAt returnează elementul de la indexul i și true, sau valoarea zero și false.
func ElementAt[T any](items []T, i int) (T, bool) {
if i < 0 || i >= len(items) {
var zero T
return zero, false
}
return items[i], true
}
func main() {
words := []string{"one", "two", "three", "four", "five"}
v1, ok1 := ElementAt(words, 1)
fmt.Printf("%q %v\n", v1, ok1)
// Indexul 5 nu există: fără panic, doar ok == false.
v2, ok2 := ElementAt(words, 5)
fmt.Printf("%q %v\n", v2, ok2)
}
Element de la o poziție, sau o valoare de rezervă
ElementAtOrElse primește o funcție de rezervă. Funcția primește indexul, așa că poate construi un mesaj precum „Valoarea pentru indexul 5 este nedefinită”.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// ElementAtOrElse returnează elementul de la indexul i, sau rezultatul lui fallback(i).
func ElementAtOrElse[T any](items []T, i int, fallback func(int) T) T {
if i < 0 || i >= len(items) {
return fallback(i)
}
return items[i]
}
func main() {
words := []string{"one", "two", "three", "four", "five"}
fmt.Println(ElementAtOrElse(words, 5, func(i int) string {
return fmt.Sprintf("Valoarea pentru indexul %d este nedefinită", i)
}))
}
Obținerea după condiție
De multe ori nu știi poziția. În schimb, știi o condiție: primul cuvânt mai lung de 3 litere, ultimul cuvânt care începe cu „f”. Condiția este o funcție care primește un element și returnează true sau false.
Pentru prima potrivire, pachetul slices are slices.IndexFunc(). Returnează indexul primului element care trece testul, sau -1 când niciun element nu îl trece. Apoi citești elementul cu acel index.
În slices nu există o funcție pentru ultima potrivire. O buclă for care numără descrescător de la len(words)-1 găsește ultima potrivire: prima potrivire întâlnită mergând înapoi este ultima din slice.
Dincolo de atât, Go nu are funcții de căutare gata făcute. Micile helpere generice Find și FindLast fac treaba și returnează value, ok, așa că „nu s-a potrivit nimic” înseamnă doar ok == false.
Prima potrivire
slices.IndexFunc() dă indexul primului cuvânt cu len(s) > 3. Acel cuvânt este "three".
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
)
func main() {
words := []string{"one", "two", "three", "four", "five"}
// IndexFunc dă indexul primului element care trece testul.
i := slices.IndexFunc(words, func(s string) bool { return len(s) > 3 })
fmt.Println(words[i])
}
Ultima potrivire
Bucla începe de la ultimul index și coboară. Primul cuvânt întâlnit care începe cu "f" este "five", deci bucla îl afișează și se oprește cu break.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
words := []string{"one", "two", "three", "four", "five"}
// Parcurgem de la sfârșit, deci prima potrivire găsită este ultima din slice.
for i := len(words) - 1; i >= 0; i-- {
if strings.HasPrefix(words[i], "f") {
fmt.Println(words[i])
break
}
}
}
Prima potrivire, sau ok == false
Niciun cuvânt nu este mai lung de 6 litere, deci Find returnează ok == false și programul afișează „nu a fost găsit”. În slice-ul cu numere, primul număr par este 2.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// Find returnează primul element care trece testul și true, sau valoarea zero și false.
func Find[T any](items []T, match func(T) bool) (T, bool) {
for _, v := range items {
if match(v) {
return v, true
}
}
var zero T
return zero, false
}
func main() {
words := []string{"one", "two", "three", "four", "five"}
numbers := []int{1, 2, 3, 4}
if w, ok := Find(words, func(s string) bool { return len(s) > 6 }); ok {
fmt.Println(w)
} else {
fmt.Println("nu a fost găsit")
}
if n, ok := Find(numbers, func(n int) bool { return n%2 == 0 }); ok {
fmt.Println(n)
}
}
Ultima potrivire, sau ok == false
FindLast parcurge numerele înapoi. Ultimul număr par este 4 și este găsit imediat.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// FindLast parcurge înapoi și returnează ultimul element care trece testul.
func FindLast[T any](items []T, match func(T) bool) (T, bool) {
for i := len(items) - 1; i >= 0; i-- {
if match(items[i]) {
return items[i], true
}
}
var zero T
return zero, false
}
func main() {
numbers := []int{1, 2, 3, 4}
if n, ok := FindLast(numbers, func(n int) bool { return n%2 == 0 }); ok {
fmt.Println(n)
}
}
Obținerea cu un selector
Uneori vrei să transformi mai întâi fiecare element și abia apoi să iei primul rezultat care se potrivește.
Go nu are o astfel de funcție. O buclă for face asta: convertește fiecare element, testează rezultatul și oprește-te cu break la primul rezultat bun. Slice-ul de aici are tipul []any, deci poate conține în același timp un număr, un string și un bool. fmt.Sprint() transformă orice valoare în text.
Prima valoare convertită care se potrivește
Fiecare valoare este transformată în text cu fmt.Sprint(). 0 devine "0", care este prea scurt. "true" are 4 caractere, deci este afișat și bucla se oprește.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
list := []any{0, "true", false}
// Transformă fiecare valoare în text și oprește-te la primul text cu 4 sau mai multe caractere.
for _, v := range list {
s := fmt.Sprint(v)
if len(s) >= 4 {
fmt.Println(s)
break
}
}
}
Element aleator
Ca să alegi un element aleator, alege un index aleator. Pachetul math/rand dă numere aleatoare; r.Intn(n) returnează un număr de la 0 până la n-1, adică exact intervalul indexurilor valide.
Go nu are o metodă random() pentru slice-uri, deci indexul intră între paranteze pătrate, ca de obicei.
O sursă aleatoare creată cu rand.NewSource(42) pornește de la un seed fix. Cu același seed, programul alege de fiecare dată același element, ceea ce face rezultatul previzibil. Lasă seed-ul deoparte când vrei un rezultat diferit la fiecare rulare.
Alege un element aleator
Cu seed-ul 42, sursa alege indexul 1, deci programul afișează 2. Schimbă seed-ul și poate ieși alt număr.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"math/rand"
)
func main() {
numbers := []int{1, 2, 3, 4}
// Un seed fix face ca alegerea „aleatoare” să fie aceeași la fiecare rulare.
r := rand.New(rand.NewSource(42))
i := r.Intn(len(numbers))
fmt.Println(numbers[i])
}
Verificarea existenței unui element
Ca să verifici dacă o valoare se află într-un slice, folosește slices.Contains(). Returnează true sau false.
Go nu are containsAll(). Un mic helper generic parcurge valorile căutate și apelează slices.Contains() pentru fiecare. Returnează false de îndată ce o valoare lipsește.
Go nu are nici isEmpty(), nici isNotEmpty(). Compară lungimea: len(s) == 0 înseamnă că este gol, len(s) > 0 înseamnă că nu este gol.
Conține o valoare
"four" se află în slice, deci prima verificare este true. "zero" nu se află, deci a doua verificare este false.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
)
func main() {
words := []string{"one", "two", "three", "four", "five"}
fmt.Println(slices.Contains(words, "four"))
fmt.Println(slices.Contains(words, "zero"))
}
Conține toate valorile
Și "four", și "one" se află în slice, deci ContainsAll returnează true. "zero" lipsește, deci al doilea apel returnează false.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
)
// ContainsAll este true doar când fiecare valoare căutată se află în items.
func ContainsAll[T comparable](items []T, wanted []T) bool {
for _, w := range wanted {
if !slices.Contains(items, w) {
return false
}
}
return true
}
func main() {
words := []string{"one", "two", "three", "four", "five"}
fmt.Println(ContainsAll(words, []string{"four", "one"}))
fmt.Println(ContainsAll(words, []string{"one", "zero"}))
}
Gol sau cu elemente
empty are lungimea 0, deci len(empty) == 0 este true. words are 5 elemente, deci și len(words) > 0 este true.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
words := []string{"one", "two", "three", "four", "five"}
empty := []string{}
// Un slice este gol când lungimea lui este 0.
fmt.Println(len(empty) == 0)
fmt.Println(len(words) > 0)
}