Treci la conținutul principal

Operații de agregare

O operație de agregare transformă o colecție întreagă într-o singură valoare. Exemple sunt numărul de elemente, suma lor, cel mai mare element sau toate elementele combinate într-un singur rezultat.

Go îți dă direct câteva dintre acestea. len() numără elementele. slices.Min() și slices.Max() din pachetul slices găsesc cel mai mic și cel mai mare element. Pentru sumă sau medie nu există funcție predefinită: aduni elementele într-o buclă for.

Ai grijă: slices.Min() și slices.Max() dau panic (opresc programul cu eroare) pe un slice gol. Verifică întâi len(s) > 0 dacă slice-ul ar putea fi gol.

Multe limbaje au metode fold și reduce pentru a combina elementele unul câte unul. Go nu are astfel de metode pe slice-uri. În schimb, scrii o singură dată un mic helper generic și îl refolosești. Helperul Fold de mai jos funcționează pentru un slice de orice tip.

Sintaxa helperului generic Fold

T este tipul elementului, iar R este tipul rezultatului. acc este acumulatorul: pornește de la initial, iar f îl combină pe rând cu fiecare element. Ultima valoare a lui acc este rezultatul.

main.go
func Fold[T, R any](items []T, initial R, f func(R, T) R) R {
  acc := initial
  for _, v := range items {
    acc = f(acc, v)
  }
  return acc
}

Agregări de bază

Cele mai des folosite agregări sunt numărul de elemente, minimul, maximul, suma și media.

  • len(numbers) dă numărul de elemente.
  • slices.Min(numbers) și slices.Max(numbers) dau cel mai mic și cel mai mare element. Funcționează pentru numere și pentru șiruri de caractere.
  • Suma este o buclă care adună fiecare element într-o variabilă. Media este suma împărțită la numărul de elemente. Convertește-le întâi pe amândouă la float64, altfel împărțirea aruncă partea zecimală.

Uneori, cel mai mic sau cel mai mare element trebuie ales după o regulă, nu după valoarea în sine. slices.MinFunc() și slices.MaxFunc() primesc o funcție de comparare. Funcția primește două elemente și returnează un număr negativ, zero sau un număr pozitiv. cmp.Compare() din pachetul cmp scrie asta pentru tine.

Uneori, fiecare element trebuie transformat întâi cu o funcție, iar rezultatele trebuie adunate. Go nu are o funcție predefinită pentru asta, așa că exemplul folosește un mic helper generic SumOf.

Număr de elemente, min, max, sumă și medie

Pentru numerele 6, 42, 10 și 4, afișează numărul de elemente cu len(), cel mai mare și cel mai mic element cu slices.Max() și slices.Min(), iar suma dintr-o buclă. Media este 62 / 4 = 15.5.

Prompt
Ia numerele 6, 42, 10 și 4. Afișează numărul de elemente, cel mai mare, cel mai mic, media și suma, fiecare pe câte o linie, cu o etichetă precum „Număr:”. Folosește slices.Max și slices.Min pentru cel mai mare și cel mai mic, iar pentru sumă o buclă.
Ieșire: program Go și rezultatul capturat
Count, min, max, sum and average

Min și max după o regulă

slices.MinFunc() compară resturile a%3 și b%3. Resturile numerelor 5, 42, 10 și 4 sunt 2, 0, 1 și 1, deci câștigă 42. slices.MaxFunc() compară lungimile șirurilor de caractere și găsește "three".

Prompt
Ia numerele 5, 42, 10 și 4 și afișează elementul cu cel mai mic rest la împărțirea cu 3, folosind slices.MinFunc. Apoi ia cuvintele one, two, three, four și afișează-l pe cel mai lung, folosind slices.MaxFunc cu o comparație după lungime.
Ieșire: program Go și rezultatul capturat
Min and max by a rule

Suma unei funcții aplicate fiecărui element

SumOf aplică funcția pe fiecare element și adună rezultatele. Dublarea numerelor 5, 42, 10 și 4 dă 122. Înjumătățirea lor ca float dă 30.5.

Prompt
Scrie un helper generic SumOf care transformă fiecare element al unui slice într-un număr cu ajutorul unei funcții și adună rezultatele. Pentru numerele 5, 42, 10 și 4 afișează suma fiecărui element înmulțit cu 2, apoi suma fiecărui element împărțit la 2 ca float.
Ieșire: program Go și rezultatul capturat
Sum of a function of each element

Fold și Reduce

Fold și Reduce combină toate elementele într-o singură valoare, pas cu pas. Ele păstrează o valoare curentă numită acumulator. Pentru fiecare element apelează o funcție cu acumulatorul și elementul, iar funcția returnează noul acumulator.

Diferența este valoarea de start:

  • Fold pornește de la o valoare inițială pe care i-o dai tu. Funcția rulează o dată pentru fiecare element.
  • Reduce pornește de la primul element. Funcția rulează începând cu al doilea element, deci primul element nu trece niciodată prin ea.

Asta contează atunci când funcția modifică elementul. Dacă ea dublează fiecare element, Reduce lasă primul element așa cum este.

Go nu are nici fold, nici reduce predefinite. Amândouă sunt mici helpere generice cu o buclă for înăuntru. Ca să faci fold de la dreapta, bucla pornește pur și simplu de la ultimul index și coboară până la 0.

Reduce

Reduce ia 5 ca start și adună la el 2, 10 și 4. Rezultatul este 21.

Prompt
Scrie un helper generic Reduce care pornește de la primul element al unui slice și combină în el elementele rămase, cu ajutorul unei funcții. Folosește-l ca să aduni numerele 5, 2, 10 și 4 și afișează rezultatul.
Ieșire: program Go și rezultatul capturat
Reduce

Fold

Fold pornește de la 0 și adună fiecare element înmulțit cu 2. Fiecare element este dublat: 10 + 4 + 20 + 8 = 42.

Prompt
Scrie un helper generic Fold care pornește de la o valoare inițială și combină în ea fiecare element al unui slice, cu ajutorul unei funcții. Folosește-l pe numerele 5, 2, 10 și 4 cu valoarea inițială 0, adunând fiecare element înmulțit cu 2, și afișează rezultatul.
Ieșire: program Go și rezultatul capturat
Fold

Fold față de Reduce

Amândouă helperele folosesc aceeași funcție, suma plus elementul înmulțit cu 2. Reduce dă 37, pentru că primul element, 5, este startul și nu este dublat. Fold cu valoarea inițială 0 dă 42.

Prompt
Folosește atât un helper Reduce, cât și un helper Fold pe numerele 5, 2, 10 și 4, cu aceeași operație: suma plus elementul înmulțit cu 2. Afișează rezultatul lui Reduce și rezultatul lui Fold, cu etichete, ca să arăți că Reduce nu dublează niciodată primul element.
Ieșire: program Go și rezultatul capturat
Fold versus Reduce

Fold de la dreapta

FoldRight parcurge slice-ul de la ultimul element spre primul. Adunarea nu ține cont de ordine, așa că rezultatul este din nou 42.

Prompt
Scrie un helper FoldRight care parcurge un slice de la ultimul element spre primul, pornind de la o valoare inițială. Folosește-l pe numerele 5, 2, 10 și 4 cu valoarea inițială 0, adunând fiecare element înmulțit cu 2, și afișează rezultatul.
Ieșire: program Go și rezultatul capturat
Fold from the right

Fold cu index

Uneori contează poziția unui element, nu doar valoarea lui.

În Go, o buclă simplă îți dă deja indexul. for idx, element := range numbers merge de la început. for idx := len(numbers) - 1; idx >= 0; idx-- merge de la sfârșit. În interiorul buclei, un if verifică indexul și decide dacă elementul este adunat.

Fold cu index

Sunt adunate doar elementele de la indexuri pare: indexul 0 conține 5, iar indexul 2 conține 10, deci suma este 15.

Prompt
Parcurge cu range numerele 5, 2, 10 și 4, ca să obții atât indexul, cât și valoarea. Adună doar elementele al căror index este par și afișează suma.
Ieșire: program Go și rezultatul capturat
Fold with index

Fold cu index de la dreapta

Aceeași regulă, dar bucla pornește de la indexul 3 și coboară până la 0. Rezultatul este tot 15.

Prompt
Parcurge numerele 5, 2, 10 și 4 de la ultimul index până la primul. Adună doar elementele al căror index este par și afișează suma.
Ieșire: program Go și rezultatul capturat
Fold with index from the right

Reduce în siguranță

Reduce are nevoie de un prim element de la care să pornească. Pe un slice gol, items[0] dă panic și programul se oprește cu eroare.

Go nu are null pentru numere. Modul în care se face în Go este să returnezi două valori: rezultatul și un bool numit ok. Când slice-ul este gol, ok este false, iar rezultatul este valoarea zero a tipului, de exemplu 0 pentru int. Este același tipar value, ok pe care îl știi de la map-uri.

Reduce cu o valoare ok

ReduceOK returnează 21 true pentru numerele 5, 2, 10 și 4 și 0 false pentru un slice gol.

Prompt
Scrie un helper generic ReduceOK care returnează valoarea redusă și un bool. Pentru un slice gol returnează false, în loc să dea panic. Adună numerele 5, 2, 10 și 4 și afișează valoarea și boolul, apoi fă la fel cu un slice gol.
Ieșire: program Go și rezultatul capturat
Reduce with an ok value

Fold și Reduce cu valori parțiale

Un fold sau un reduce cu valori parțiale nu aruncă pașii intermediari. El salvează acumulatorul după fiecare element, deci rezultatul este un slice cu toate valorile parțiale.

În Go ții o variabilă pentru sumă și o adaugi cu append() într-un slice de rezultate, în interiorul buclei. Pentru un reduce cu valori parțiale, rezultatul începe cu primul element. Pentru un fold cu valori parțiale, rezultatul începe cu valoarea inițială.

Reduce cu valori parțiale

Numerele de la 0 la 5 sunt adunate unul câte unul. Slice-ul rezultat conține fiecare sumă de pe parcurs: [0 1 3 6 10 15].

Prompt
Ia numerele 0, 1, 2, 3, 4 și 5. Construiește un slice de sume parțiale: pornește de la primul element și adaugă suma după fiecare element următor. Afișează slice-ul.
Ieșire: program Go și rezultatul capturat
Running reduce

Fold cu valori parțiale

La fel, dar cu valoarea inițială 10. Rezultatul începe chiar cu 10 și are cu un element mai mult decât intrarea: [10 10 11 13 16 20 25].

Prompt
Ia numerele 0, 1, 2, 3, 4 și 5. Construiește un slice de sume parțiale care pornește de la valoarea inițială 10 și adaugă suma după fiecare element. Afișează slice-ul.
Ieșire: program Go și rezultatul capturat
Running fold