Výčtový typ - Enumerated type

V programování počítače , což je výčtového typu (také nazývaný výčtu , výčtu nebo faktoru v R programovací jazyk , a kategorické proměnné ve statistikách) je datový typ skládající se ze sady pojmenovaných hodnot nazývaných prvky , členové , enumeral nebo komisaři z typ. Jména enumerátorů jsou obvykle identifikátory, které se v jazyce chovají jako konstanty . Výčtového typu může být viděn jako degenerovaná značeného unie z typu jednotky . Proměnná , která byla prohlášena za mající výčtu typu lze přiřadit některý z komisařů jako hodnota. Jinými slovy, vyjmenovaný typ má hodnoty, které se navzájem liší a které lze porovnávat a přiřazovat, ale nejsou programátorem specifikovány jako konkrétní konkrétní reprezentace v paměti počítače; kompilátoři a tlumočníci je mohou libovolně zastupovat.

Například čtyři obleky v balíčku hracích karet mohou být čtyři sčítači pojmenovaní Club , Diamond , Heart a Spade , patřící do vyjmenovaného typu pojmenovaného oblek . Pokud je proměnná V deklarována jako vyhovující jako datový typ, lze jí přiřadit libovolnou z těchto čtyř hodnot.

Ačkoli jsou výčtové obvykle odlišní, některé jazyky mohou umožnit, aby byl stejný deklarátor uveden dvakrát v deklaraci typu. Jména enumerátorů nemusí být sémanticky úplná nebo kompatibilní v žádném smyslu. Například může být definován výčtový typ nazvaný barva, který bude sestávat z enumerátorů Red , Green , Zebra , Missing a Bacon . V některých jazycích deklarace výčtového typu také záměrně definuje uspořádání jeho členů; v jiných jsou sčítači neuspořádáni; v ostatních stále implicitní řazení vyplývá z kompilátoru konkrétně reprezentujícího enumerátory jako celá čísla.

Některé typy enumerátorů mohou být integrovány do jazyka. Například booleovský typ je často předdefinovaný výčet hodnot False a True . Mnoho jazyků umožňuje uživatelům definovat nové vyjmenované typy.

Hodnoty a proměnné výčtového typu jsou obvykle implementovány jako bitové řetězce s pevnou délkou , často ve formátu a velikosti kompatibilní s některým celočíselným typem. Některé jazyky, zejména systémové programovací jazyky , umožňují uživateli určit bitovou kombinaci, která se má použít pro každý enumerátor. V teorii typů jsou výčtové typy často považovány za označené svazky typů jednotek . Protože tyto typy jsou ve formě , mohou být také zapsány jako přirozená čísla.

Zdůvodnění

Některé rané programovací jazyky původně neměly vyjmenované typy. Pokud by programátor chtěl, aby proměnná, například myColor , měla hodnotu červené, byla by proměnná červená deklarována a přiřazena jí libovolná hodnota, obvykle celočíselná konstanta. Proměnná červená by pak byla přiřazena k myColor . Jiné techniky přiřazovaly řetězcům obsahujícím názvy enumerátorů libovolné hodnoty.

Tyto libovolné hodnoty byly někdy označovány jako magická čísla, protože často nebylo vysvětleno, jak byla čísla získána nebo zda byly jejich skutečné hodnoty významné. Tato magická čísla by ostatním mohla ztížit pochopení a údržbu zdrojového kódu.

Výčtové typy na druhé straně činí kód více samodokumentujícím. V závislosti na jazyce by kompilátor mohl automaticky přiřadit výchozí hodnoty enumerátorům a skrýt tak zbytečné detaily před programátorem. Tyto hodnoty nemusí být pro programátora ani viditelné (viz skrývání informací ). Enumerované typy mohou také zabránit programátorovi ve psaní nelogického kódu, jako je provádění matematických operací s hodnotami enumerátorů. Pokud by měla být vytištěna hodnota proměnné, které byl přiřazen enumerátor, některé programovací jazyky by také mohly vytisknout název enumerátoru spíše než jeho základní číselnou hodnotu. Další výhodou je, že vyjmenované typy mohou kompilátorům umožnit vynutit sémantickou správnost. Například: myColor = TRIANGLE může být zakázáno, zatímco myColor = RED je přijímáno, i když jsou TRIANGLE a RED vnitřně zastoupeny jako 1 .

Výčtový typ je koncepčně podobný seznamu nominálních hodnot (číselných kódů), protože každé možné hodnotě typu je přiřazeno rozlišovací přirozené číslo. Daný vyjmenovaný typ je tedy konkrétní implementací tohoto pojmu. Pokud je pořadí smysluplné a/nebo slouží ke srovnání, pak se z výčtového typu stane pořadový typ.

Konvence

Programovací jazyky mívají své vlastní, často mnohonásobné, programovací styly a konvence pojmenování . Proměnná přiřazená k výčtu je obvykle podstatné jméno v jednotném čísle a často následuje konvenci PascalCase nebo velká písmena , zatímco malá písmena a další jsou vidět méně často.

Syntaxe v několika programovacích jazycích

Pascal a syntakticky podobné jazyky

Pascal

V Pascalu lze výčtový typ implicitně deklarovat vypsáním hodnot v seznamu v závorkách:

  var
    suit: (clubs, diamonds, hearts, spades);

Deklarace se často objeví v deklaraci synonyma typu, takže ji lze použít pro více proměnných:

  type
    cardsuit = (clubs, diamonds, hearts, spades);
    card = record
             suit: cardsuit;
             value: 1 .. 13;
           end;
  var
    hand: array [ 1 .. 13 ] of card;
    trump: cardsuit;

Na pořadí, ve kterém jsou uvedeny hodnoty výčtu, záleží. Výčtový typ je řadový typ preda succfunkce a poskytne předchozí nebo další hodnotu výčtu a ordmůže převést hodnoty výčtu na celočíselnou reprezentaci. Standard Pascal však nenabízí převod z aritmetických typů na výčty. Extended Pascal nabízí tuto funkci prostřednictvím rozšířené succfunkce. Některé jiné Pascalské dialekty to umožňují pomocí typových odlitků. Někteří moderní potomci Pascalu, jako například Modula-3 , poskytují speciální syntaxi převodu pomocí metody zvané VAL; Modula-3 také léčí BOOLEANa CHARjako speciální předem definovaných vyjmenovaných typů a použití ORDa VALpro standardní ASCII dekódování a kódování.

Jazyky ve stylu Pascal také umožňují použít výčet jako index pole:

  var
    suitcount: array [cardsuit] of integer;

Ada

V Ada bylo použití „=“ nahrazeno „is“, takže definice byla docela podobná:

type Cardsuit is (clubs, diamonds, hearts, spades);

Kromě toho Pred, Succ, Vala PosAda také podporuje jednoduché řetězce konverze prostřednictvím Imagea Value.

Podobně jako v jazycích ve stylu C umožňuje Ada zadat vnitřní reprezentaci výčtu:

for Cardsuit use
  (clubs => 1, diamonds => 2, hearts => 4, spades => 8);

Na rozdíl od jazyků ve stylu C Ada také umožňuje zadat počet bitů výčtu:

for Cardsuit'Size use 4;  -- 4 bits

Navíc lze použít výčty jako indexy pro pole, jako v Pascalu, ale pro výčty jsou definovány atributy

   Shuffle : constant array(Cardsuit) of Cardsuit :=
     (Clubs => Cardsuit'Succ(Clubs), -- see attributes of enumerations 'First, 'Last, 'Succ, 'Pred
      Diamonds => Hearts, --an explicit value
      Hearts => Cardsuit'Last, --first enumeration value of type Cardsuit e.g., clubs
      Spades => Cardsuit'First --last enumeration value of type Cardsuit e.g., spades
      );

Jako Modula-3 Ada zachází Booleana Characterjako speciální předem definované (v balíčku " Standard") vyjmenované typy. Na rozdíl od Modula-3 lze také definovat vlastní typy znaků:

type Cards is ('7', '8', '9', 'J', 'Q', 'K', 'A');

C a syntakticky podobné jazyky

C

Původní dialekt K&R programovacího jazyka C neměl žádné vyjmenované typy. V jazyce C jsou výčty vytvářeny explicitními definicemi ( enumklíčové slovo samo o sobě nezpůsobuje přidělení úložiště), které používají enumklíčové slovo a připomínají definice struktury a sjednocení :

enum cardsuit {
    Clubs,
    Diamonds,
    Hearts,
    Spades
};

struct card {
    enum cardsuit suit;
    short int value;
} hand[13];

enum cardsuit trump;

C vystavuje celočíselnou reprezentaci hodnot výčtu přímo programátoru. Celá čísla a hodnoty výčtu lze libovolně kombinovat a všechny aritmetické operace s hodnotami výčtu jsou povoleny. Je dokonce možné, aby proměnná výčtu obsahovala celé číslo, které nepředstavuje žádnou z hodnot výčtu. Ve skutečnosti, podle definice jazyka, výše uvedený kód bude definovat Clubs, Diamonds, Hearts, a Spadesjako konstanty typu int, které budou převedeny pouze (tiché) se enum cardsuitv případě, že jsou uloženy v proměnné tohoto typu.

C také umožňuje programátorovi explicitně zvolit hodnoty konstant výčtu, a to i bez typu. Například,

enum cardsuit {
    Clubs    = 1,
    Diamonds = 2,
    Hearts   = 4,
    Spades   = 8
};

lze použít k definování typu, který umožňuje, aby matematické sady barev byly reprezentovány jako enum cardsuitbitové logické operace.

C#

Výčtové typy v programovacím jazyce C# zachovávají většinu sémantiky „malých celých čísel“ výčtů jazyka C. Některé aritmetické operace nejsou pro výčty definovány, ale hodnotu výčtu lze explicitně převést na celé číslo a zpět a proměnná výčtu může mít hodnoty, které nebyly definovány výčtem. Například dané

enum Cardsuit
{
    Clubs,
    Diamonds,
    Spades,
    Hearts
}

výrazy CardSuit.Diamonds + 1a CardSuit.Hearts - CardSuit.Clubsjsou povoleny přímo (protože může mít smysl procházet posloupnost hodnot nebo se ptát, kolik kroků je mezi dvěma hodnotami), ale CardSuit.Hearts * CardSuit.Spadespovažuje se za méně smysluplné a je povoleno pouze tehdy, pokud jsou hodnoty nejprve převedeny na celá čísla .

C# také poskytuje funkci podobnou C, že je možné definovat konkrétní celočíselné hodnoty pro výčty. Tímto způsobem je možné provádět binární operace s výčty, takže hodnoty výčtu budou považovány za sady příznaků. Tyto příznaky lze testovat pomocí binárních operací nebo pomocí vestavěné metody 'HasFlag' typu Enum.

Definice výčtu definuje názvy pro vybrané celočíselné hodnoty a je syntaktickým cukrem , protože je možné přiřadit proměnné výčtu jiné celočíselné hodnoty, které nejsou v rozsahu definice výčtu.

C ++

C ++ má typy výčtu, které jsou přímo zděděny z C a fungují většinou jako tyto, kromě toho, že výčet je skutečný typ v C ++, což poskytuje přidanou kontrolu při kompilaci. Také (stejně jako u struktur) je enumklíčové slovo C ++ automaticky kombinováno s typedef , takže místo pojmenování typu jej enum namejednoduše pojmenujte name. To lze simulovat v jazyce C pomocí typedef:typedef enum {Value1, Value2} name;

C ++ 11 poskytuje druhý typ typově bezpečný výčet, který není implicitně převeden na celočíselný typ. Umožňuje pro tento typ definovat streamování io. Výčty navíc nevrací, takže musí být použity s výčtem Type::enumeration. To je specifikováno frází „třída výčtu“. Například:

enum class Color {Red, Green, Blue};

Základní typ je implementace definovaný integrální typ, který je dostatečně velká, aby držet všechny vyjmenované hodnoty (to nemusí být co nejmenší typ!). V C ++ můžete zadat základní typ přímo. To umožňuje "předat deklarace" výčtů:

enum class Color : long {Red, Green, Blue};  // must fit in size and memory layout the type 'long'
enum class Shapes : char;  // forward declaration. If later there are values defined that don't fit in 'char' it is an error.

Jít

Go používá iotaklíčové slovo k vytvoření výčtových konstant.

type ByteSize float64

const (
    _           = iota // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota)
    MB
    GB
)

Jáva

J2SE verze 5.0 programovacího jazyka Java přidala vyjmenované typy, jejichž syntaxe deklarace je podobná jako u C :

enum Cardsuit { CLUBS, DIAMONDS, SPADES, HEARTS };
...
Cardsuit trump;

Systém typu Java však považuje výčty za typ oddělený od celých čísel a smísení hodnot výčtu a celých čísel není povoleno. Ve skutečnosti je typ výčtu v Javě ve skutečnosti speciální třídou generovanou kompilátorem, nikoli aritmetickým typem, a hodnoty výčtu se chovají jako globální předem vygenerované instance této třídy. Typy výčtu mohou mít metody instance a konstruktor (jehož argumenty lze zadat samostatně pro každou hodnotu výčtu). Všechny typy výčtu implicitně rozšiřují Enumabstraktní třídu. Typ výčtu nelze přímo vytvořit instanci.

Interně každá hodnota výčtu obsahuje celé číslo, odpovídající pořadí, ve kterém jsou deklarovány ve zdrojovém kódu, počínaje od 0. Programátor nemůže nastavit vlastní celé číslo pro hodnotu výčtu přímo, ale lze definovat přetížené konstruktory, které pak mohou přiřadit libovolné hodnoty pro vlastní členy třídy výčtu. Definování getterů pak umožňuje přístup k těmto samostatně definovaným členům. Interní celé číslo lze získat z hodnoty výčtu pomocí ordinal()metody a seznam hodnot výčtu typu výčtu lze získat v pořadí pomocí values()metody. Obecně se programátorům nedoporučuje převádět výčty na celá čísla a naopak. Výčet typů je Comparablepomocí interního celého čísla; v důsledku toho je lze třídit.

Standardní knihovna Java poskytuje pomocné třídy pro použití s ​​výčty. Tyto EnumSettřídy implementuje Sethodnot výčtu; je implementován jako bitové pole , díky čemuž je velmi kompaktní a efektivní jako explicitní manipulace s bitem, ale bezpečnější. Tyto EnumMaptřídy implementuje Maphodnot výčtu k objektu. Je implementován jako pole, přičemž celočíselná hodnota hodnoty výčtu slouží jako index.

Perl

Dynamicky psané jazyky v syntaktické tradici C (např. Perl nebo JavaScript ) obecně neposkytují výčty. Ale v programování v Perlu lze stejného výsledku dosáhnout pomocí zkráceného seznamu řetězců a hashů (případně řezů ):

my @enum = qw(Clubs Diamonds Hearts Spades);
my( %set1, %set2 );
@set1{@enum} = ();          # all cleared
@set2{@enum} = (1) x @enum; # all set to 1
$set1{Clubs} ...            # false
$set2{Diamonds} ...         # true

Raku

Raku (dříve známý jako Perl 6) podporuje výčty. Existuje několik způsobů, jak deklarace výčtů v Raku, všechny vytvořit back-end Map.

enum Cat <sphynx siamese bengal shorthair other>; # Using "quote-words"
enum Cat ('sphynx', 'siamese', 'bengal', 'shorthair', 'other'); # Using a list
enum Cat (sphynx => 0, siamese => 1, bengal => 2, shorthair => 3, other => 4); # Using Pair constructors
enum Cat (:sphynx(0), :siamese(1), :bengal(2), shorthair(3), :other(4)); # Another way of using Pairs, you can also use `:0sphynx`

PHP

Výčty byly přidány v PHP verze 8.1.

enum CardSuit
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;
}

Rez

Ačkoli Rust používá enumklíčové slovo jako C, používá ho k popisu označených svazků , které mohou být pokládány za degenerovanou formu. Rustovy výčty jsou proto mnohem flexibilnější a mohou obsahovat strukturní a řazené varianty.

enum Message {
    Quit,
    Move { x: i32, y: i32 }, // struct
    Write(String), // single-element tuple
    ChangeColor(i32, i32, i32), // three-element tuple
}

Rychlý

V jazyce C výčty přiřazují související názvy sadě celočíselných hodnot. V Swiftu jsou výčty mnohem flexibilnější a nemusí poskytovat hodnotu pro každý případ výčtu. Pokud je pro každý případ výčtu uvedena hodnota (označovaná jako nezpracovaná hodnota), může to být řetězec, znak nebo hodnota jakéhokoli typu s celým číslem nebo s plovoucí desetinnou čárkou.

Alternativně mohou případy výčtu specifikovat přidružené hodnoty libovolného typu, které mají být uloženy spolu s každou jinou hodnotou případu, podobně jako to dělají odbory nebo varianty v jiných jazycích. Jeden může definovat společnou sadu souvisejících případů jako součást jednoho výčtu, z nichž každý má s ním spojenou jinou sadu hodnot příslušných typů.

Ve Swiftu jsou výčty prvotřídního typu. Přijímají mnoho funkcí tradičně podporovaných pouze třídami, jako jsou například vypočítané vlastnosti, které poskytují další informace o aktuální hodnotě výčtu, a metody instance, které poskytují funkce související s hodnotami, které výčet představuje. Enumerations can also define initializers to provide an initial case value and can be extended to expand their functionality beyond their original implementation; a mohou odpovídat protokolům, aby poskytovaly standardní funkce.

enum CardSuit {
    case clubs
    case diamonds
    case hearts
    case spades
}

Na rozdíl od C a Objective-C nejsou případům výčtu Swift při vytváření přiřazena výchozí celočíselná hodnota. Ve výše uvedeném příkladu CardSuit se kluby, diamanty, srdce a piky implicitně nerovná 0, 1, 2 a 3. Místo toho jsou různé případy výčtu plně hodnotami samy o sobě, s explicitně definovaným typem CardSuit .

Na jednom řádku se může objevit více případů oddělených čárkami:

enum CardSuit {
    case clubs, diamonds, hearts, spades
}

Při práci s výčty, které ukládají celočíselné nebo řetězcové nezpracované hodnoty, není nutné explicitně přiřazovat nezpracovanou hodnotu pro každý případ, protože Swift hodnoty automaticky přiřadí.

Pokud jsou například pro nezpracované hodnoty použita celá čísla, implicitní hodnota pro každý případ je o jedno více než v předchozím případě. Pokud první případ nemá nastavenou hodnotu, je jeho hodnota 0.

Níže uvedený výčet je upřesněním dřívějšího výčtu Planet s celočíselnými nezpracovanými hodnotami, které představují pořadí každé planety od Slunce:

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

Ve výše uvedeném příkladu má Planet.mercury explicitní surovou hodnotu 1, Planet.venus má implicitní surovou hodnotu 2 atd.

„Podrobnosti najdete v dokumentaci Swift online zde.“

Strojopis

TypeScript přidává do JavaScriptu datový typ „enum“.

enum Cardsuit {Clubs, Diamonds, Hearts, Spades};
var c: Cardsuit = Cardsuit.Diamonds;

Ve výchozím nastavení vyjmenovává počet členů od 0; toto lze přepsat nastavením hodnoty prvního:

enum Cardsuit {Clubs = 1, Diamonds, Hearts, Spades};
var c: Cardsuit = Cardsuit.Diamonds;

Všechny hodnoty lze nastavit:

enum Cardsuit {Clubs = 1, Diamonds = 2, Hearts = 4, Spades = 8};
var c: Cardsuit = Cardsuit.Diamonds;

Strojopis podporuje mapování číselné hodnoty na její název. Například se tím najde název hodnoty 2:

enum Cardsuit {Clubs = 1, Diamonds, Hearts, Spades};
var suitName: string = Cardsuit[2];

alert(suitName);

Krajta

enumModul byl přidán do standardní knihovny Python ve verzi 3.4.

from enum import Enum
class Cards(Enum):
    CLUBS = 1
    DIAMONDS = 2
    HEARTS = 3
    SPADES = 4

Existuje také funkční API pro vytváření výčtů s automaticky generovanými indexy (počínaje jedním):

Cards = Enum('Cards', 'CLUBS DIAMONDS HEARTS SPADES'])

Výčty Pythonu nevynucují sémantickou správnost (nesmyslné srovnání s nekompatibilním výčtem vždy vrací False místo vyvolání TypeError ):

>>> Color = Enum("Color", "RED GREEN BLUE")
>>> Shape = Enum("Shape", ["CIRCLE", "TRIANGLE", "SQUARE", "HEXAGON"])
>>> def has_vertices(shape):
... 	return shape != Shape.CIRCLE
...
>>> has_vertices(Color.GREEN)
True

Fortran

Fortran má pouze vyjmenované typy pro interoperabilitu s C; sémantika je tedy podobná C a stejně jako v C jsou hodnoty výčtu pouze celá čísla a neprovádí se žádná další kontrola typu. Příklad C shora lze zapsat do Fortranu jako

enum, bind( C )
  enumerator :: CLUBS = 1, DIAMONDS = 2, HEARTS = 4, SPADES = 8
end enum

Visual Basic/VBA

Výčtovým datovým typům v jazyce Visual Basic (až do verze 6) a VBA je automaticky přiřazen Longdatový typ " " a také se stanou datovým typem:

'Zero-based
Enum CardSuit
    Clubs
    Diamonds
    Hearts
    Spades
End Enum

Sub EnumExample()
    Dim suit As CardSuit
    suit = Diamonds
    MsgBox suit
End Sub

Příklad kódu ve VB.NET

Enum CardSuit
    Clubs
    Diamonds
    Hearts
    Spades
End Enum

Sub EnumExample()
    Dim suit As CardSuit
    suit = CardSuit.Diamonds
    MessageBox.show(suit)
End Sub

Lisp

Common Lisp používá specifikátor typu člena, např.

(deftype cardsuit ()
  '(member club diamond heart spade))

to uvádí, že předmět je typu cardsuit, pokud jde o palici #'eql, diamant, srdce nebo rýč. Specifikátor typu člena však není platný jako specializovaný parametr parametru CLOS ( Common Lisp Object System ). Místo toho může být použit (eql atom)ekvivalent, což je ekvivalent (member atom)(tj. Může být specifikován pouze jeden člen sady se specifikátorem typu eql, ale může být použit jako specializovaný parametr parametru CLOS.) Jinými slovy, definovat metody pro pokrytí výčtového typu musí být pro každý konkrétní prvek tohoto typu definována metoda.

Dodatečně,

(deftype finite-element-set-type (&rest elements)
   `(member ,@elements))

lze použít k definování libovolných vyjmenovaných typů za běhu. Například

(finite-element-set-type club diamond heart spade)

by odkazoval na typ ekvivalentní předchozí definici cardsuit, jak by samozřejmě jednoduše používali

(member club diamond heart spade)

ale může být méně matoucí s funkcí #'memberze stylistických důvodů.

Algebraický datový typ ve funkčním programování

Ve funkčních programovacích jazycích v linii ML (např. Standard ML (SML), OCaml a Haskell ) lze k implementaci vyjmenovaného typu použít algebraický datový typ pouze s nulovými konstruktory . Například (v syntaxi podpisů SML):

datatype cardsuit = Clubs | Diamonds | Hearts | Spades
type card = { suit: cardsuit; value: int }
val hand : card list
val trump : cardsuit

V těchto jazycích je malá celočíselná reprezentace zcela skrytá před programátorem, pokud taková reprezentace skutečně implementace využívá. Haskell však má Enum třídu typu, kterou může typ odvodit nebo implementovat, aby získal mapování mezi typem a Int.

Databáze

Některé databáze podporují výčtové typy přímo. MySQL poskytuje výčtový typ ENUMs přípustnými hodnotami zadanými jako řetězce při vytváření tabulky. Hodnoty jsou uloženy jako číselné indexy, přičemž prázdný řetězec je uložen jako 0, hodnota prvního řetězce je uložena jako 1, hodnota druhého řetězce je uložena jako 2 atd. Hodnoty lze ukládat a načítat jako číselné indexy nebo hodnoty řetězců.

Příklad:

CREATE TABLE shirts (
    name VARCHAR(40),
    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);

XML schéma

Schéma XML podporuje výčtové typy prostřednictvím fazety výčtu používané pro omezení většiny primitivních datových typů, jako jsou řetězce.

<xs:element name="cardsuit">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:enumeration value="Clubs"/>
      <xs:enumeration value="Diamonds"/>
      <xs:enumeration value="Hearts"/>
      <xs:enumeration value="Spades"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Viz také

Reference

externí odkazy