Wprowadzenie do typów generycznych
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 67 z 107.
Przed Go 1.18 pisanie wielokrotnego użytku kodu dla różnych typów oznaczało albo powielanie funkcji, albo używanie pustego interfejsu any z asercjami typów. Typy generyczne rozwiązują ten problem, pozwalając pisać funkcje i typy działające z wieloma typami przy zachowaniu pełnego bezpieczeństwa typów.
Rozważ znalezienie najmniejszej wartości w wycinku. Bez typów generycznych potrzebne byłyby osobne funkcje dla każdego typu:
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Dzięki typom generycznym zapisujesz logikę raz, używając parametru typu w nawiasach kwadratowych:
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}[T int | float64] deklaruje parametr typu T, który może być typu int lub float64. Teraz możesz wywołać Min([]int{3, 1, 2}) lub Min([]float64{3.5, 1.2}) za pomocą tej samej funkcji. Kompilator sprawdza typy podczas kompilacji, dzięki czemu zyskujesz bezpieczeństwo bez narzutu w czasie działania.
W kolejnych lekcjach szczegółowo omówimy parametry typów, ograniczenia i struktury generyczne.
Wyzwanie
ŁatwyStwórzmy analizator wyników, który obsługuje zarówno wyniki całkowite, jak i zmiennoprzecinkowe, wykorzystując typy generyczne! Utworzysz jedną funkcję generyczną, która znajdzie największą wartość w wycinku, eliminując potrzebę powielania kodu dla różnych typów liczbowych.
Rozmieścisz kod w dwóch plikach:
analyzer.go: Zdefiniuj funkcję generyczną do analizy.Utwórz funkcję generyczną
Max[T int | float64](values []T) T, która znajduje i zwraca największą wartość w wycinku. Dzięki parametrowi typuTfunkcja powinna działać zarówno z typemint, jak ifloat64.Utwórz także funkcję generyczną
Sum[T int | float64](values []T) T, która oblicza i zwraca sumę wszystkich wartości w wycinku.main.go: Wczytaj dane wejściowe i zaprezentuj działanie funkcji generycznych z różnymi typami.Wczytaj oznaczenie typu (
intlubfloat), a następnie wczytaj liczbę wartości i podaną liczbę wartości. Parsuj wartości zgodnie z typem, wywołaj zarównoMax, jak iSumz odpowiednim typem, a następnie wypisz wyniki.Dla danych wejściowych typu całkowitego wypisz:
Max (int): [value] Sum (int): [value]Dla danych wejściowych typu zmiennoprzecinkowego wypisz wartości z jednym miejscem po przecinku:
Max (float): [value] Sum (float): [value]
Otrzymasz następujące dane wejściowe:
- Wiersz 1: Oznaczenie typu (
intlubfloat) - Wiersz 2: Liczba wartości (liczba całkowita)
- Kolejne wiersze: Jedna wartość w każdym wierszu
Na przykład, dla danych:
int
4
15
8
23
11Wynik powinien być następujący:
Max (int): 23
Sum (int): 57A dla danych:
float
3
4.5
9.2
6.8Wynik powinien być następujący:
Max (float): 9.2
Sum (float): 20.5Zwróć uwagę, że te same funkcje generyczne obsługują zarówno dane całkowite, jak i zmiennoprzecinkowe: parametr typu [T int | float64] pozwala kompilatorowi generować bezpieczny typowo kod dla każdego użycia, podczas gdy logikę zapisujesz tylko raz.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Odczytaj wskaźnik typu
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Odczytaj liczbę elementów
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Odczytaj wartości całkowite
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Wywołaj Max i Sum, przekazując wycinek liczb całkowitych
// TODO: Wypisz wyniki w formacie "Max (int): [value]" i "Sum (int): [value]"
} else if typeStr == "float" {
// Odczytaj wartości zmiennoprzecinkowe
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Wywołaj Max i Sum, przekazując wycinek wartości float64
// TODO: Wypisz wyniki z jednym miejscem po przecinku w formacie "Max (float): [value]" i "Sum (float): [value]"
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online