Přepnout prohlášení - Switch statement
V počítačových programovacích jazycích je příkaz switch typem mechanismu řízení výběru, který umožňuje hodnotě proměnné nebo výrazu změnit řídicí tok provádění programu pomocí vyhledávání a mapy.
Přepínací příkazy fungují poněkud podobně jako if
příkazy používané v programovacích jazycích jako C / C ++ , C# , Visual Basic .NET , Java a existují ve většině imperativních programovacích jazyků na vysoké úrovni, jako jsou Pascal , Ada , C / C ++ , C# , Visual Basic. NET , Java , a v mnoha jiných typů jazyka, používání takových klíčových slov , jako switch
, case
, select
nebo inspect
.
Příkazy přepínačů se dodávají ve dvou hlavních variantách: strukturovaný přepínač, jako v Pascalu, který má přesně jednu větev, a nestrukturovaný přepínač, jako v jazyce C, který funguje jako typ goto . Mezi hlavní důvody pro použití přepínače patří zlepšení srozumitelnosti snížením jinak opakujícího se kódování a (pokud to heuristika dovoluje) také nabízí potenciál pro rychlejší provedení díky snazší optimalizaci překladače v mnoha případech.
switch (age) {
case 1: printf("You're one."); break;
case 2: printf("You're two."); break;
case 3: printf("You're three.");
case 4: printf("You're three or four."); break;
default: printf("You're not 1,2,3 or 4!");
}
|
Dějiny
V jeho 1952 textu Úvod do metamathematics , Stephen Kleene formálně prokázáno, že funkce CASE (IF-THEN-ELSE funkce je nejjednodušší forma) je primitivní rekurzivní funkce , kde se definuje pojem definition by cases
následujícím způsobem:
- "#F. Takto definovaná funkce φ."
- φ (x 1 , ..., x n ) =
- φ 1 (x 1 , ..., x n ) pokud Q 1 (x 1 , ..., x n ),
- . . . . . . . . . . . .
- φ m (x 1 , ..., x n ) pokud Q m (x 1 , ..., x n ),
- φ m+1 (x 1 , ..., x n ) jinak,
- φ (x 1 , ..., x n ) =
- kde Q 1 , ..., Q m jsou vzájemně se vylučující predikáty (nebo φ (x 1 , ..., x n ) musí mít hodnotu danou první klauzulí, která platí) je primitivní rekurzivní v φ 1 , ... , φ m+1 , Q 1 , ..., Q m+1 .
Kleene je toho důkazem ve smyslu booleovských rekurzivních funkcí „sign-of“ sg () a „not sign of“ ~ sg () (Kleene 1952: 222-223); první vrátí 1, pokud je jeho vstup kladný, a −1, pokud je jeho vstup záporný.
Boolos-Burgess-Jeffrey učinil další poznámku, že „definice podle případů“ musí být vzájemně se vylučující a kolektivně vyčerpávající. I oni nabízejí důkaz primitivní rekurzivity této funkce (Boolos-Burgess-Jeffrey 2002: 74-75).
IF-THEN-ELSE je základem McCarthyho formalismu : jeho použití nahrazuje jak primitivní rekurzi, tak mu-operátor .
Typická syntaxe
Ve většině jazyků programátoři píší příkaz switch na mnoha jednotlivých řádcích pomocí jednoho nebo dvou klíčových slov. Typická syntaxe zahrnuje:
- první
select
, za kterým následuje výraz, který je často označován jako kontrolní výraz nebo ovládací proměnná příkazu switch - následující řádky definující skutečné případy (hodnoty), s odpovídajícími sekvencemi příkazů pro provedení v případě shody
- V jazycích s přechodovým chováním
break
příkaz obvykle následuje zacase
příkazem k ukončení uvedeného prohlášení. [Wells]
Každá alternativa začíná konkrétní hodnotu, nebo seznam hodnot (viz níže), že řídící proměnné může odpovídat, a které způsobí, že ovládací prvek Goto odpovídající posloupnost příkazů. Hodnota (nebo seznam/rozsah hodnot) je obvykle oddělena od odpovídající sekvence příkazů dvojtečkou nebo implikační šipkou. V mnoha jazycích musí každému případu také předcházet klíčové slovo jako case
nebo when
.
Volitelný standardní případ je typicky také povoleno, specifikována default
, otherwise
nebo else
klíčového slova. Spustí se, když žádný z ostatních případů neodpovídá výrazu ovládacího prvku. V některých jazycích, například C, pokud se žádný případ neshoduje a default
je vynechán, switch
příkaz jednoduše skončí. V jiných, jako PL/I, je vyvolána chyba.
Sémantika
Sémanticky existují dvě hlavní formy příkazů přepínače.
První formou jsou strukturované přepínače, jako v Pascalu, kde je brána přesně jedna větev, a případy jsou považovány za samostatné, exkluzivní bloky. Funguje to jako generalizovaná podmínka , pokud - pak - jinak , zde s libovolným počtem větví, nejen se dvěma.
Druhou formou jsou nestrukturované přepínače, jako v C, kde jsou případy zpracovány jako popisky v rámci jednoho bloku a přepínač funguje jako generalizovaný přechod. Toto rozlišení se označuje jako zacházení s propadem, které je popsáno níže.
Propadnout
V mnoha jazycích je spuštěn pouze odpovídající blok a potom provádění pokračuje na konci příkazu switch. Patří sem rodina Pascalů (Object Pascal, Modula, Oberon, Ada atd.) A také PL/I , moderní formy dialektů Fortran a BASIC ovlivněné Pascalem, většinou funkčních jazyků a mnoha dalšími. Aby bylo možné spouštět stejný kód více hodnotami (a vyhnout se nutnosti duplikovat kód ), jazyky typu Pascal povolují libovolný počet hodnot na případ, udávaný jako seznam oddělený čárkami, jako rozsah nebo jako kombinace.
Jazyky odvozené z jazyka C, a obecněji ty, které jsou ovlivněny vypočítaným GOTO od Fortranu , místo toho obsahují propad, kde se ovládání přesune na odpovídající případ a poté pokračuje provádění („propadá“) do příkazů spojených s dalším případem ve zdrojovém textu . To také umožňuje, aby více hodnot odpovídalo stejnému bodu bez speciální syntaxe: jsou uvedeny pouze s prázdnými těly. Hodnoty mohou být speciálně podmíněné kódem v těle pouzdra. V praxi se obvykle propadu zabrání break
klíčovým slovem na konci odpovídajícího těla, které ukončí provádění bloku přepínačů, ale to může způsobit chyby v důsledku neúmyslného propadu, pokud programátor zapomene vložit break
příkaz. To je tedy mnohými vnímáno jako jazyková bradavice a varováno před některými nástroji na odstraňování vláken. Syntakticky jsou případy interpretovány jako popisky, nikoli bloky a příkazy switch a break výslovně mění tok řízení. Některé jazyky ovlivněné jazykem C, například JavaScript , si zachovávají výchozí propad, zatímco jiné pád procházejí, nebo jej povolují pouze za zvláštních okolností. Pozoruhodné variace na toto v C-rodině zahrnují C# , ve kterém musí být všechny bloky zakončeny a break
nebo return
pokud není blok prázdný (tj. Jako způsob určení více hodnot se používá propad).
V některých případech jazyky poskytují volitelný přechod. Například Perl ve výchozím nastavení nepropadá, ale případ to může výslovně udělat pomocí continue
klíčového slova. Tím se zabrání neúmyslnému propadnutí, ale v případě potřeby to umožní. Podobně Bash ve výchozím nastavení ;;
nepropadne, když skončí s , ale povolí propad s ;&
nebo ;;&
místo.
Příkladem příkazu switch, který se spoléhá na propad, je zařízení Duff .
Sestavení
Optimalizace kompilátorů, jako je GCC nebo Clang, může zkompilovat příkaz switch do větvící tabulky nebo binárního vyhledávání hodnotami v případech. Odvětvová tabulka umožňuje příkazu switch určit pomocí malého, konstantního počtu instrukcí, kterou větev provést, aniž byste museli procházet seznamem srovnání, zatímco binární vyhledávání trvá pouze logaritmický počet srovnání, měřeno počtem případů v příkaz switch.
Normálně je jediným způsobem, jak zjistit, zda k této optimalizaci došlo, skutečným pohledem na výslednou sestavu nebo výstup strojového kódu , který byl generován kompilátorem.
Výhody a nevýhody
V některých jazycích a programovacích prostředích je použití příkazu a case
nebo switch
považováno za lepší než ekvivalentní řada příkazů if else if, protože je:
- Jednodušší ladění (např. Nastavení zarážek na kódu vs. tabulka volání, pokud ladicí program nemá schopnost podmíněného zarážky)
- Čtení pro člověka je snazší
- Snadněji pochopitelné a následně snadněji udržovatelné
- Opravená hloubka: sekvence příkazů „if else if“ může vést k hlubokému vnoření, což komplikuje kompilaci (zejména v automaticky generovaném kódu)
- Jednodušší ověření, že jsou zpracovány všechny hodnoty. Kompilátory mohou vydat varování, pokud nejsou zpracovány některé hodnoty výčtu.
Navíc je optimalizovaná implementace může vykonat mnohem rychleji než druhá možnost, protože to je často realizována pomocí indexovaných větev tabulky . Například rozhodování o toku programu na základě hodnoty jednoho znaku, je -li správně implementováno, je mnohem efektivnější než alternativa, což značně zkracuje délku cesty instrukce . Při implementaci jako takové se příkaz switch v podstatě stane dokonalým hashem .
Pokud jde o graf toku toku , příkaz přepínače se skládá ze dvou uzlů (vstup a výstup) plus jedné hrany mezi nimi pro každou možnost. Naopak sekvence příkazů „if ... else if ... else if“ má další uzel pro každý jiný případ než první a poslední, spolu s odpovídající hranou. Výsledný graf řídicího toku pro sekvence „if“ s má tedy mnohem více uzlů a téměř dvakrát tolik hran, přičemž tyto nepřidávají žádné užitečné informace. Jednoduché větve v příkazech if jsou však koncepčně jednodušší než komplexní větev příkazu switch. Pokud jde o cyklomatickou složitost , obě tyto možnosti ji zvýší o k −1, pokud je dáno k případů.
Přepnout výrazy
Výrazy přepínačů jsou v Java SE 12 , 19. března 2019, zavedeny jako funkce náhledu. Zde lze pro vrácení hodnoty použít celý výraz přepínače. K dispozici je také nová forma označení případu, case L->
kde na pravé straně je jeden výraz. To také zabraňuje pádu a vyžaduje, aby případy byly vyčerpávající. V jazyce Java SE 13 je tento yield
příkaz zaveden a v jazyce Java SE 14 se výrazy přepínačů stávají funkcí standardního jazyka. Například:
int ndays = switch(month) {
case JAN, MAR, MAY, JUL, AUG, OCT, DEC -> 31;
case APR, JUN, SEP, NOV -> 30;
case FEB -> {
if(year % 400 ==0) yield 29;
else if(year % 100 == 0) yield 28;
else if(year % 4 ==0) yield 29;
else yield 28; }
};
Alternativní použití
Mnoho jazyků vyhodnocuje výrazy uvnitř switch
bloků za běhu, což umožňuje řadu méně zřejmých použití pro konstrukci. To zakazuje určité optimalizace kompilátoru, takže je běžnější v dynamických a skriptovacích jazycích, kde je vylepšená flexibilita důležitější než režie výkonu.
PHP
Například v PHP lze konstantu použít jako „proměnnou“ ke kontrole a provede se první příkaz případu, který tuto konstantu vyhodnotí:
switch (true) {
case ($x == 'hello'):
foo();
break;
case ($z == 'howdy'): break;
}
switch (5) {
case $x: break;
case $y: break;
}
Tato funkce je také užitečná pro kontrolu více proměnných oproti jedné hodnotě místo jedné proměnné podle mnoha hodnot. COBOL také podporuje tento formulář (a další formuláře) v EVALUATE
prohlášení. PL/I má alternativní formu SELECT
příkazu, kde je ovládací výraz zcela vynechán a je proveden první, WHEN
který vyhodnotí hodnotu true .
Rubín
V Ruby , kvůli jeho zpracování ===
rovnosti, lze příkaz použít k testování třídy proměnné:
case input
when Array then puts 'input is an Array!'
when Hash then puts 'input is a Hash!'
end
Ruby také vrací hodnotu, kterou lze přiřadit proměnné, a ve skutečnosti nevyžaduje, case
aby měla nějaké parametry (působí trochu jako else if
příkaz):
catfood =
case
when cat.age <= 1
junior
when cat.age > 10
senior
else
normal
end
Montér
Přepínač v jazyce sestavení :
switch:
cmp ah, 00h
je a
cmp ah, 01h
je b
jmp swtend ; No cases match or "default" code here
a:
push ah
mov al, 'a'
mov ah, 0Eh
mov bh, 00h
int 10h
pop ah
jmp swtend ; Equivalent to "break"
b:
push ah
mov al, 'b'
mov ah, 0Eh
mov bh, 00h
int 10h
pop ah
jmp swtend ; Equivalent to "break"
...
swtend:
Zpracování výjimek
Řada jazyků implementuje formu příkazu switch při zpracování výjimek , kde pokud je v bloku vyvolána výjimka, je vybrána samostatná větev v závislosti na výjimce. V některých případech je k dispozici také výchozí větev, pokud není vyvolána žádná výjimka. Raným příkladem je Modula-3 , který používá TRY
... EXCEPT
syntaxi, kde každý EXCEPT
definuje případ. To je také v Delphi , Scala a Visual Basic.NET .
Alternativy
Některé alternativy k přepnutí příkazů mohou být:
- Série podmíněných podmínek if-else, které zkoumají cílovou hodnotu najednou. Přepadového chování lze dosáhnout pomocí sekvence if podmíněných bez klauzule else .
- Vyhledávací tabulka , která obsahuje, jako klíče, o
case
hodnotách a jako hodnoty, v části podcase
prohlášení.
- (V některých jazycích jsou jako hodnoty ve vyhledávací tabulce povoleny pouze skutečné datové typy. V jiných jazycích je také možné přiřadit funkce jako hodnoty vyhledávací tabulky a získat stejnou flexibilitu jako skutečné
switch
prohlášení. Podrobnější informace naleznete v článku Ovládací tabulka Na toto). -
Lua nepodporuje příkazy case/switch: http://lua-users.org/wiki/SwitchStatement . Tato vyhledávací technika je jedním ze způsobů implementace
switch
příkazů v jazyce Lua, který nemá žádný vestavěnýswitch
. - V některých případech jsou vyhledávací tabulky účinnější než neoptimalizované
switch
příkazy, protože mnoho jazyků může optimalizovat vyhledávání tabulek, zatímco příkazy přepínače nejsou optimalizovány, pokud není rozsah hodnot malý s několika mezerami. Non-optimalizované, non- binární vyhledávání vyhledávání, nicméně, téměř jistě bude pomalejší než jeden přepínač non-optimalizovaná nebo ekvivalentní násobku if-else závěrky.
- (V některých jazycích jsou jako hodnoty ve vyhledávací tabulce povoleny pouze skutečné datové typy. V jiných jazycích je také možné přiřadit funkce jako hodnoty vyhledávací tabulky a získat stejnou flexibilitu jako skutečné
- Kontrolní stůl (která může být provedena jako jednoduchá vyhledávací tabulka) může být také přizpůsoben pro uložení více podmínek na více vstupů v případě potřeby a obvykle vykazuje větší ‚vizuální kompaktnosti‘, než ekvivalentní přepínač (které mohou zabírat mnoho příkazy).
- Pattern matching , který se používá k implementaci funkcí podobných přepínačům v mnoha funkčních jazycích.
Viz také
Reference
Další čtení
- Stephen Kleene , 1952 (10. dotisk 1991), Úvod do metamatematiky , North-Holland Publishing Company, Amsterdam NL, ISBN 0-7204-2103-9
- George Boolos , John Burgess a Richard Jeffrey , 2002, Computability and Logic: Fourth Edition , Cambridge University Press, Cambridge UK, ISBN 0-521-00758-5 brož. srov. strana 74-75.