Modül dersleri (3/5)
Jenerikler (1.18+ sürümüne geçin)
Go 1.18'den (Mart 2022) bu yana tür parametreleri mevcuttur: işlevler ve türler, türler üzerinden parametrelendirilebilir. Kodu çoğaltmadan ve interface{} + çalışma zamanı türü iddiaları olmadan Map, Filter, Set[T], LinkedList[T] yazmanıza izin verirler.
Genel işlev: sözdizimi
func Map[T, U any](s []T, f func(T) U) []U {
out := make([]U, len(s))
for i, v := range s {
out[i] = f(v)
}
return out
}
doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// doubled is []int{2, 4, 6}Üç yeni unsur:
[T, U any]addan sonra: tür parametrelerini kısıtlamalarıyla birlikte bildirir.any= herhangi bir türü kabul eden kısıtlama (Go 1.18'den bu yanainterface{}takma adı).- Çıkarım:
Map([]int{...}, ...)'yi çağırdığınızdaMap[int, int](...)yazmanıza gerek yoktur: derleyiciT=int, U=intsonucunu çıkarır.
Bazen çıkarım yeterli olmaz (örneğin, T bağımsız değişkeni olmayan yapıcılar) ve açık örneklemeye ihtiyaç duyulur: New[*Server]().
Yerleşik kısıtlamalar
| Kısıtlama | Anlamı |
|---|---|
any | Herhangi bir tür (== interface{}). |
comparable | == ve != içeren türler: sayısal, dize, bool, işaretçiler, kanallar, karşılaştırılabilir diziler/yapılar. |
comparable eşitliğe izin verir ancak değil < veya >.
func Index[T comparable](s []T, x T) int {
for i, v := range s {
if v == x { return i }
}
return -1
}</> için özel bir kısıtlamaya ihtiyacınız vardır.
Özel kısıtlama: tür birleşimleri
Kısıtlama, "type element" cümleciklerini içeren bir arayüz'dür:
type Ordered interface {
~int | ~int64 | ~float64 | ~string
}
func Min[T Ordered](a, b T) T {
if a < b { return a }
return b
}|= birleşim: listelenen türlerden herhangi birini kabul eder.~int= "int veyatype X intolarak tanımlanan herhangi bir tür" (aynı temel türle).~olmadan yalnızca tamint.
Bir kısıtlama ayrıca yöntemleri ve tür öğelerini karıştırabilir:
type Stringable interface {
~string
Len() int
}Genel türler
Yalnızca işlevler değil, yapılar ve arayüzler de tür parametrelerine sahip olabilir.
type Set[T comparable] struct {
m map[T]struct{}
}
func NewSet[T comparable]() *Set[T] {
return &Set[T]{m: make(map[T]struct{})}
}
func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }
s := NewSet[string]()
s.Add("ada")Yöntemler türün dışındaki tür parametreleri EKLEYEMEZ: func (s *Set[T]) MapTo[U any](...) derlemez (kasıtlı tasarım sınırlaması).
Jenerikler ne zaman kullanılmalı?
Evet ne zaman:
- Mantık, çeşitli türler için gerçekten aynıdır (koleksiyonlar, algoritmalar,
Map/Filter/Reducegibi yardımcılar). - Bunun alternatifi, kodu çoğaltmak veya
interface{}+ tür iddialarını kullanmak (tür güvenliğini kaybetmek) olabilir.
Hayır şu durumlarda:
- Bir veya iki metoda sahip bir arayüz yeterlidir ve daha okunaklıdır (
io.Reader,fmt.Stringer). - "Çünkü yapabilirsin" parametresini belirliyorsunuz: tek kullanımlık bir durum → bunun somut bir versiyonunu yapın.
Egzersizler
Genel işlevi tanımlayın Min[T Ordered](a, b T) Operatör <'yi kullanarak küçük işlemi gerçekleştirin. Kısıtlama Düzenlendi ve çok güzel.
3 denemeden sonra çözüm mevcut
Harita[T, U herhangi](s []T, f func(T) U) []U: bir öğenin uygulanması ve yeni dilimin yeniden oluşturulması.
3 denemeden sonra çözüm mevcut
Nitelik kısıtlaması yerleşik operatör onayı == ma NON operatör <?
func Index[T ???](s []T, x T) int { ... }