Mashqlar
Bu bo’limda generics’ni amalda mustahkamlaysiz: type parameter, type inference, constraint (comparable, cmp.Ordered, union, ~) va generic turlar. Har mashqni avval o’zingiz yozib ishga tushiring, keyin yechimni oching. Kod Go 1.21+ (cmp paketi) uchun.
1. Generic First
Slice’ning birinchi elementini qaytaradigan generic First[T any] funksiyasini yozing. Bo’sh slice bo’lsa T ning nol qiymatini va false qaytarsin. Kutilgan chiqish quyidagicha.
Yechimni ko’rish
package main
import "fmt"
func First[T any](s []T) (T, bool) {
var zero T // T ning nol qiymati — qaysi tur bo'lsa ham ishlaydi
if len(s) == 0 {
return zero, false
}
return s[0], true
}
func main() {
fmt.Println(First([]int{10, 20, 30})) // type inference: T=int
fmt.Println(First([]string{})) // bo'sh slice
}$ go run first.go
10 true
falseBo’sh string slice uchun nol qiymat "" (bo’sh satr), shuning uchun ikkinchi qatorda false dan oldin bo’shliq ko’rinadi. First([]int{...}) deb yozmadik — Go turni argumentdan o’zi aniqladi.
2. Map — bir turdan boshqasiga
[]T ni []U ga aylantiradigan Map[T, U any](s []T, f func(T) U) []U yozing. Uni int slice’ni har elementi ikki barobar string ga aylantirishda ishlating. Kirish []int{1,2,3} uchun kutilgan chiqish: [2x 4x 6x].
Yechimni ko’rish
package main
import (
"fmt"
"strconv"
)
// Ikkita type parameter: kirish T va chiqish U.
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
func main() {
nums := []int{1, 2, 3}
out := Map(nums, func(n int) string {
return strconv.Itoa(n*2) + "x"
})
fmt.Println(out)
}$ go run map.go
[2x 4x 6x]T=int, U=string — ikkovini ham type inference argumentlardan aniqladi, Map[int, string] deb yozish shart emas.
3. Sum — union constraint bilan
int ham, float64 ham qabul qiladigan Sum yozing. Buning uchun ~int | ~float64 union constraint kerak. type Ball float64 kabi maxsus tur ham ~ tufayli mos kelsin. Kutilgan chiqish quyidagicha.
Yechimni ko’rish
package main
import "fmt"
// ~int | ~float64 — asosiy turi shu bo'lgan barcha turlar.
type Number interface {
~int | ~float64
}
type Ball float64 // asosiy turi float64
func Sum[T Number](nums []T) T {
var total T // nol qiymat: int uchun 0, float64 uchun 0.0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(Sum([]int{1, 2, 3, 4}))
fmt.Println(Sum([]float64{1.5, 2.5}))
fmt.Println(Sum([]Ball{3.5, 4.5})) // ~float64 bo'lgani uchun mos
}$ go run sum.go
10
4
8~ bo’lmaganida Sum([]Ball{...}) kompilyatsiya bo’lmasdi — Ball alohida tur, lekin asosiy turi float64.
4. Index — comparable bilan
Slice ichidan berilgan elementning indeksini qaytaradigan Index[T comparable] yozing (topilmasa -1). == ishlatgani uchun constraint comparable bo’lishi shart. Kutilgan chiqish quyidagicha.
Yechimni ko’rish
package main
import "fmt"
// comparable — == va != bilan solishtirsa bo'ladigan turlar.
func Index[T comparable](s []T, target T) int {
for i, v := range s {
if v == target { // comparable shuni mumkin qiladi
return i
}
}
return -1
}
func main() {
fmt.Println(Index([]string{"a", "b", "c"}, "b")) // 1
fmt.Println(Index([]int{5, 6, 7}, 9)) // topilmadi
}$ go run index.go
1
-1comparable o’rniga any yozsak, v == target kompilyatsiya bo’lmasdi — har qanday tur == ni qo’llab-quvvatlamaydi (masalan slice’larni solishtirib bo’lmaydi).
5. Generic Stack[T]
Istalgan tur bilan ishlaydigan Stack[T any] yasang: Push va Pop metodlari bo’lsin. Pop bo’sh stack’da nol qiymat va false qaytarsin. Kutilgan chiqish quyidagicha.
Yechimni ko’rish
package main
import "fmt"
type Stack[T any] struct {
items []T
}
// Receiver'da T ni qayta yozamiz: (s *Stack[T]).
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if len(s.items) == 0 {
return zero, false
}
last := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return last, true
}
func main() {
st := &Stack[int]{} // instantiation: Stack[int]
st.Push(1)
st.Push(2)
fmt.Println(st.Pop()) // 2 true
fmt.Println(st.Pop()) // 1 true
fmt.Println(st.Pop()) // bo'sh: 0 false
}$ go run stack.go
2 true
1 true
0 falseStack[int] va Stack[string] alohida turlar; st ga string push qilib bo’lmaydi — kompilyator to’xtatadi.
6. MaxBy — cmp.Ordered kaliti bilan
Struct slice’dan berilgan kalit bo’yicha eng kattasini topadigan MaxBy yozing. Element turi T any, kalit turi esa cmp.Ordered bo’lsin (K ni argument funksiya qaytaradi). Talabalar ichidan eng yuqori bahoni toping. Kutilgan chiqish: Vali 90.
Yechimni ko’rish
package main
import (
"cmp"
"fmt"
)
// T — element turi (any), K — kalit turi (tartiblanadigan).
func MaxBy[T any, K cmp.Ordered](s []T, key func(T) K) T {
best := s[0]
for _, v := range s[1:] {
if key(v) > key(best) { // K cmp.Ordered bo'lgani uchun > joiz
best = v
}
}
return best
}
type Student struct {
Name string
Grade int
}
func main() {
students := []Student{
{"Ali", 75},
{"Vali", 90},
{"Guli", 82},
}
top := MaxBy(students, func(s Student) int { return s.Grade })
fmt.Println(top.Name, top.Grade)
}$ go run maxby.go
Vali 90Ikki type parameter (T va K) va bitta constraint (K cmp.Ordered) birga ishladi. Student ni to’g’ridan-to’g’ri solishtirib bo’lmaydi, lekin uning Grade kaliti tartiblanadi — shuning uchun key funksiyasini uzatdik.