Ordinare i tipi integrati
slices.Sort ordina qualsiasi slice il cui tipo di elemento sia ordinabile: interi, float e stringhe. Ordina sul posto e non restituisce nulla.
Output:
[3 7 19 42 88]
[Bob Carl alice lisa]
true
2 true
Le stringhe si ordinano per byte, quindi ogni lettera ASCII maiuscola viene prima di ogni minuscola. Per i nomi raramente è ciò che un utente si aspetta. La prossima sezione risolve il problema.
Il pacchetto slices è arrivato con Go 1.21. Il suo Sort è un pattern-defeating quicksort: O(n log n), sul posto e non stabile.
Ordine personalizzato con SortFunc
slices.SortFunc accetta una funzione di confronto func(a, b T) int. Restituisci un numero negativo quando a deve venire prima, un numero positivo quando deve venire prima b e zero quando sono uguali. La funzione cmp.Compare restituisce esattamente questo per i tipi ordinabili.
strings.ToLower dentro un confronto alloca una nuova stringa ogni volta che l'input contiene una maiuscola, e il confronto viene eseguito circa n log n volte. Per slice grandi, calcola le chiavi in minuscolo una sola volta. Per nomi in altre lingue (accenti, regole locali) usa golang.org/x/text/collate, che è fuori dalla libreria standard.
Una funzione di confronto deve essere coerente: se dice che a viene prima di b, deve dire che b viene dopo a. Una che viola questa regola (per esempio restituendo -1 ogni volta che due valori sono diversi) produce uno slice ordinato male senza alcun errore. Sottrarre interi (return a - b) sembra elegante ma va in overflow con valori grandi; usa cmp.Compare.
Ordinare struct
La funzione di confronto riceve gli elementi, quindi ordinare struct per un campo è la stessa chiamata:
Qui i dipendenti con lo stesso stipendio possono uscire in qualsiasi ordine, perché SortFunc non è stabile. Se ti serve un ordine garantito, risolvi i pareggi con altri campi oppure usa un ordinamento stabile.
Ordinare per più campi
Confronta prima il campo più importante e passa al successivo solo in caso di pareggio. cmp.Or (Go 1.22) restituisce il primo argomento diverso da zero, e questo riduce tutto a una riga:
Output:
eng 150 Cy
eng 120 Ana
eng 120 Eve
sales 90 Bob
sales 90 Dee
Ogni confronto viene valutato anche quando il primo decide, dato che sono semplici argomenti. Per i confronti tra campi costa poco. Quando un criterio di spareggio è costoso, scrivi a mano la catena if c := ...; c != 0 { return c }.
Ordinamento stabile
Un ordinamento stabile mantiene nel loro ordine originale gli elementi che risultano uguali. Conta quando l'input ha già un ordine significativo, per esempio record ordinati per tempo che ora raggruppi per utente.
Stampa [{ana 2} {ana 4} {bob 1} {bob 3} {bob 5}]: dentro ogni utente, la sequenza originale resta intatta. L'ordinamento stabile fa più lavoro, quindi usalo solo quando l'ordine degli elementi uguali conta.
Ordinare una mappa
Le mappe non hanno un ordine. Per mostrare una mappa ordinata per chiave, ordina le sue chiavi: slices.Sorted(maps.Keys(m)) (Go 1.23). Per ordinare per valore, ordina le chiavi con un confronto che legge i valori:
Lo spareggio sulla parola è importante: senza, chan e slice (entrambe 7) verrebbero stampate in un ordine diverso a ogni esecuzione, perché le chiavi escono dalla mappa in ordine casuale. Vedi mappe per saperne di più.
Il pacchetto sort: sort.Slice e simili
Prima di Go 1.21 l'ordinamento passava dal pacchetto sort. Lo troverai in molto codice esistente:
sort.Ints(nums)
sort.Strings(names)
sort.Slice(staff, func(i, j int) bool {
return staff[i].Salary < staff[j].Salary
})
sort.SliceStable(staff, func(i, j int) bool { ... })
Differenze utili da conoscere:
sort.Slice | slices.SortFunc | |
|---|---|---|
| La funzione riceve | gli indici i, j | gli elementi a, b |
| Restituisce | bool (se i è minore di j) | int (negativo, zero, positivo) |
| Sicurezza dei tipi | accetta any, usa la reflection | generica, controllata in fase di compilazione |
| Velocità | più lento | più veloce |
Un bug comune con sort.Slice è catturare nella closure uno slice diverso da quello che si sta ordinando, dato che la funzione less indicizza per posizione. SortFunc non può avere questo bug perché ti passa gli elementi.
Il tipo sort.Interface (Len, Less, Swap) è la forma più vecchia. Resta il modo per ordinare dati che non sono un singolo slice, come due slice paralleli che devono muoversi insieme. Da Go 1.22, sort.Ints, sort.Strings e sort.Float64s chiamano semplicemente slices.Sort.
Errori comuni
- Aspettarsi che
Sortrestituisca lo slice ordinato. Ordina sul posto e non restituisce nulla. Usaslices.Sorted(slices.Values(s))se vuoi un nuovo slice ordinato, oppure primaslices.Clone. - Dare per scontato che gli elementi uguali mantengano il loro ordine. Solo le varianti
Stablelo garantiscono. - Sottrarre per confrontare.
a - bva in overflow. Usacmp.Compare. - Ordinare float con NaN.
cmp.Comparemette NaN prima di ogni altro valore, e questo mantiene coerente l'ordinamento. Un confronto scritto a manoa < bno.
Domande frequenti
Come si ordina uno slice in Go?
Per numeri e stringhe chiama slices.Sort(s) (Go 1.21). Ordina sul posto in ordine crescente. Per qualsiasi altro caso, o per un ordine diverso, usa slices.SortFunc(s, func(a, b T) int { ... }), dove la funzione restituisce un numero negativo se a viene prima, positivo se viene prima b e zero se sono uguali.
Come ordino uno slice in ordine decrescente in Go?
Scambia gli argomenti nel confronto: slices.SortFunc(s, func(a, b int) int { return cmp.Compare(b, a) }). Oppure ordina in modo crescente e poi chiama slices.Reverse(s).
Come ordino uno slice di struct per più campi in Go?
Confronta il primo campo e passa al successivo solo quando è uguale. cmp.Or (Go 1.22) fa esattamente questo: return cmp.Or(cmp.Compare(a.Dept, b.Dept), cmp.Compare(b.Salary, a.Salary), strings.Compare(a.Name, b.Name)) restituisce il primo risultato diverso da zero.
Qual è la differenza tra sort.Slice e slices.SortFunc?
sort.Slice(s, func(i, j int) bool) è l'API più vecchia: accetta una funzione less sugli indici e usa la reflection per scambiare gli elementi. slices.SortFunc(s, func(a, b T) int) è generica, controllata dal compilatore, riceve direttamente gli elementi ed è più veloce. Il codice nuovo dovrebbe usare il pacchetto slices; sort.Slice è ancora comune nel codice scritto prima di Go 1.21.