Menu
Coddy logo textTech

Struktury generyczne

Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 70 z 107.

Podobnie jak funkcje, struktury również mogą mieć parametry typów. Struktura generyczna pozwala tworzyć struktury danych, które działają z dowolnym typem, zachowując bezpieczeństwo typów. To idealne rozwiązanie do tworzenia wielokrotnego użytku kontenerów, takich jak stosy, kolejki czy pary klucz-wartość.

Aby zdefiniować typ generyczny, umieść parametr typu po nazwie typu:

type Box[T any] struct {
    Value T
}

func main() {
    intBox := Box[int]{Value: 42}
    strBox := Box[string]{Value: "hello"}
    
    fmt.Println(intBox.Value)  // 42
    fmt.Println(strBox.Value)  // cześć
}

W przeciwieństwie do funkcji generycznych, podczas tworzenia instancji struktury generycznej musisz jawnie określić typ. Go nie może wywnioskować typu wyłącznie z kontekstu.

Struktury generyczne mogą mieć wiele parametrów typów, dzięki czemu idealnie nadają się do reprezentowania par lub map:

type Pair[K, V any] struct {
    Key   K
    Value V
}

p := Pair[string, int]{Key: "age", Value: 25}
fmt.Printf("%s: %d\n", p.Key, p.Value)  // age: 25

Możesz także zastosować ograniczenia, aby upewnić się, że przechowywane typy obsługują określone operacje:

type NumberBox[T int | float64] struct {
    Value T
}

nb := NumberBox[float64]{Value: 3.14}

Struktury generyczne stanowią podstawę do tworzenia bezpiecznych typowo, wielokrotnego użytku struktur danych w Go. W następnej lekcji dowiesz się, jak definiować metody dla tych typów generycznych.

challenge icon

Wyzwanie

Łatwy

Zbudujmy uniwersalny system inwentarza, który może śledzić różne typy przedmiotów! Utworzysz wielokrotnego użytku struktury kontenerów, które działają z dowolnym typem, zachowując pełne bezpieczeństwo typów.

Rozmieścisz swój kod w dwóch plikach:

  • inventory.go: Zdefiniuj swoje generyczne struktury kontenerów.

    Utwórz generyczną strukturę Item[T any] z dwoma polami: Name (string) i Data (typu T). Reprezentuje ona dowolny przedmiot z powiązanymi danymi elastycznego typu.

    Utwórz generyczną strukturę Container[T any] z jednym polem Items, które przechowuje wycinek elementów typu Item[T]. Ten kontener może przechowywać wiele przedmiotów tego samego typu danych.

    Utwórz generyczną strukturę z ograniczeniem PricedItem[T int | float64] z trzema polami: Name (string), Quantity (int) i Price (typu T). Ograniczenie gwarantuje, że ceny zawsze będą liczbami.

    Zaimplementuj funkcję NewContainer[T any]() *Container[T], która tworzy i zwraca wskaźnik do pustego Container.

    Zaimplementuj metodę Add dla *Container[T], która przyjmuje nazwę (string) i dane (T), tworzy element Item i dodaje go do wycinka Items kontenera.

    Zaimplementuj metodę Count dla Container[T], która zwraca liczbę przedmiotów w kontenerze.

  • main.go: Pokaż działanie swoich generycznych struktur na różnych typach.

    Wczytaj typ przedmiotu (string, int lub priced), a następnie wczytaj liczbę przedmiotów i ich dane.

    Dla typu string: Wczytaj pary składające się z nazwy i danych tekstowych. Utwórz Container[string], dodaj wszystkie przedmioty, a następnie wypisz każdy przedmiot jako [Name]: [Data], po czym wypisz łączną liczbę przedmiotów.

    Dla typu int: Wczytaj pary składające się z nazwy i danych będących liczbą całkowitą. Utwórz Container[int], dodaj wszystkie przedmioty, a następnie wypisz każdy przedmiot jako [Name]: [Data], po czym wypisz łączną liczbę przedmiotów.

    Dla typu priced: Wczytaj trójki składające się z nazwy, ilości i ceny (jako liczby zmiennoprzecinkowej). Utwórz bezpośrednio instancje PricedItem[float64] i wypisz każdą jako [Name] x[Quantity] @ [Price], pokazując cenę z jednym miejscem po przecinku.

    Wypisz wiersz z liczbą w formacie: Total items: [count]

Dane wejściowe będą miały następujący format:

  • Wiersz 1: Typ przedmiotu (string, int lub priced)
  • Wiersz 2: Liczba przedmiotów (liczba całkowita)
  • Kolejne wiersze: Dane przedmiotów zależne od ich typu

Na przykład dla danych:

string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined Paper

Wynik powinien wyglądać tak:

Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3

A dla danych:

int
2
Apples
50
Oranges
30

Wynik powinien wyglądać tak:

Apples: 50
Oranges: 30
Total items: 2

A dla danych:

priced
2
Widget
10
19.99
Gadget
5
49.50

Wynik powinien wyglądać tak:

Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2

Zwróć uwagę, że ta sama struktura Container działa bez problemu zarówno z ciągami znaków, jak i liczbami całkowitymi, natomiast PricedItem wykorzystuje ograniczenie, aby zapewnić, że jako ceny można stosować wyłącznie typy liczbowe. Podczas tworzenia instancji tych generycznych struktur musisz jawnie podać parametr typu.

Spróbuj swoich sił

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Odczytaj typ elementu
	scanner.Scan()
	itemType := scanner.Text()
	
	// Odczytaj liczbę elementów
	scanner.Scan()
	count, _ := strconv.Atoi(scanner.Text())
	
	switch itemType {
	case "string":
		// TODO: Utwórz Container[string] za pomocą NewContainer
		// TODO: Odczytaj 'count' elementów (każdy ma nazwę w jednym wierszu, a dane w następnym)
		// TODO: Dodaj każdy element do kontenera
		// TODO: Wypisz każdy element w formacie "[Name]: [Data]"
		// TODO: Wypisz "Total items: [count]" za pomocą metody Count
		
	case "int":
		// TODO: Utwórz Container[int] za pomocą NewContainer
		// TODO: Odczytaj 'count' elementów (każdy ma nazwę w jednym wierszu, a dane całkowite w następnym)
		// TODO: Dodaj każdy element do kontenera
		// TODO: Wypisz każdy element w formacie "[Name]: [Data]"
		// TODO: Wypisz "Total items: [count]" za pomocą metody Count
		
	case "priced":
		// TODO: Utwórz slice do przechowywania instancji PricedItem[float64]
		// TODO: Odczytaj 'count' elementów (każdy ma nazwę, ilość i cenę w osobnych wierszach)
		// TODO: Utwórz PricedItem[float64] dla każdego elementu i dodaj go do slice
		// TODO: Wypisz każdy element w formacie "[Name] x[Quantity] @ [Price]", wyświetlając cenę z jednym miejscem po przecinku
		// TODO: Wypisz "Total items: [count]"
	}
	
	_ = scanner // Użyj scanner do odczytu danych wejściowych
	_ = count   // Użyj count do sterowania pętlą
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Go online