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 pred
a succ
funkce a poskytne předchozí nebo další hodnotu výčtu a ord
můž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é succ
funkce. 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éčí BOOLEAN
a CHAR
jako speciální předem definovaných vyjmenovaných typů a použití ORD
a VAL
pro 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
, Val
a Pos
Ada také podporuje jednoduché řetězce konverze prostřednictvím Image
a 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í Boolean
a Character
jako 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 ( enum
klíčové slovo samo o sobě nezpůsobuje přidělení úložiště), které používají enum
klíč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 Spades
jako konstanty typu int
, které budou převedeny pouze (tiché) se enum cardsuit
v 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 cardsuit
bitové 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 + 1
a CardSuit.Hearts - CardSuit.Clubs
jsou 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.Spades
považ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 enum
klíčové slovo C ++ automaticky kombinováno s typedef , takže místo pojmenování typu jej enum name
jednoduš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á iota
klíč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í Enum
abstraktní 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 Comparable
pomocí 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 EnumSet
třídy implementuje Set
hodnot 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 EnumMap
třídy implementuje Map
hodnot 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á enum
klíč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
enum
Modul 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 Long
datový 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í #'member
ze 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 ENUM
s 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>