Generics
Generics — Lập trình tổng quát (Go 1.18+)
Trước Go 1.18, muốn viết hàm hoạt động với nhiều kiểu, phải dùng interface{} và type assertion. Generics cho phép viết type-safe code tổng quát.
// Không có generics — phải viết cho từng kiểu
func SumInt(s []int) int { ... }
func SumFloat(s []float64) float64 { ... }
// Với generics — viết một lần, dùng cho mọi kiểu số
func Sum[T int | float64](s []T) T {
var total T
for _, v := range s {
total += v
}
return total
}
Sum([]int{1, 2, 3}) // 6
Sum([]float64{1.5, 2.5}) // 4.0
Type Constraint
import "golang.org/x/exp/constraints"
// Định nghĩa constraint
type Number interface {
int | int8 | int16 | int32 | int64 |
float32 | float64
}
// Dùng constraint có sẵn
func Min[T constraints.Ordered](a, b T) T {
if a < b { return a }
return b
}
Min(3, 5) // 3 (int)
Min(3.14, 2.71) // 2.71 (float64)
Min("apple", "zen") // "apple" (string)
Generic Type (Struct)
// Stack generic
type Stack[T any] struct {
items []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
}
n := len(s.items) - 1
item := s.items[n]
s.items = s.items[:n]
return item, true
}
// Dùng với bất kỳ kiểu nào
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
strStack := Stack[string]{}
strStack.Push("hello")
Các hàm functional với Generics
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 Filter[T any](s []T, f func(T) bool) []T {
var result []T
for _, v := range s {
if f(v) { result = append(result, v) }
}
return result
}
func Reduce[T, U any](s []T, init U, f func(U, T) U) U {
result := init
for _, v := range s {
result = f(result, v)
}
return result
}
// Dùng
nums := []int{1, 2, 3, 4, 5}
doubled := Map(nums, func(n int) int { return n * 2 })
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
total := Reduce(nums, 0, func(acc, n int) int { return acc + n })
example.go
package main
import "fmt"
type Ordered interface {
~int | ~float64 | ~string
}
func Min[T Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func Max[T Ordered](a, b T) T {
if a > b {
return a
}
return b
}
type Stack[T any] struct{ items []T }
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Size() int { return len(s.items) }
func (s *Stack[T]) IsEmpty() bool { return len(s.items) == 0 }
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if s.IsEmpty() {
return zero, false
}
n := len(s.items) - 1
v := s.items[n]
s.items = s.items[:n]
return v, true
}
func Map[T, U any](s []T, f func(T) U) []U {
r := make([]U, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
func Filter[T any](s []T, f func(T) bool) []T {
var r []T
for _, v := range s {
if f(v) {
r = append(r, v)
}
}
return r
}
func Reduce[T, U any](s []T, init U, f func(U, T) U) U {
r := init
for _, v := range s {
r = f(r, v)
}
return r
}
func main() {
fmt.Println(Min(3, 7))
fmt.Println(Min("banana", "apple"))
s := Stack[string]{}
for _, w := range []string{"Go", "is", "awesome"} {
s.Push(w)
}
for !s.IsEmpty() {
if v, ok := s.Pop(); ok {
fmt.Print(v, " ")
}
}
fmt.Println()
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
squares := Map(nums, func(n int) int { return n * n })
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
total := Reduce(nums, 0, func(a, b int) int { return a + b })
fmt.Println("Bình phương:", squares)
fmt.Println("Số chẵn: ", evens)
fmt.Println("Tổng: ", total)
}