Řízení toku - Control flow
Smyčkové konstrukce |
---|
Ve vědě o počítačích , řídící tok (nebo proud kontroly ) je pořadí, ve kterém jednotlivé příkazy , pokyny nebo volání funkce po dosažení imperativní programu jsou prováděny nebo vyhodnocovány. Důraz na explicitní tok řízení odlišuje imperativní programovací jazyk od deklarativního programovacího jazyka.
V rámci imperativního programovacího jazyka je příkaz toku řízení příkazem, jehož výsledkem je volba, kterou ze dvou nebo více cest následovat. U nekompaktních funkčních jazyků existují funkce a jazykové konstrukce , aby se dosáhlo stejného výsledku, ale obvykle se nenazývají příkazy toku řízení.
Sada příkazů je zase obecně strukturována jako blok , který kromě seskupování definuje také lexikální rozsah .
Přerušení a signály jsou mechanismy nízké úrovně, které mohou změnit tok řízení podobným způsobem jako podprogram , ale obvykle se vyskytují jako reakce na nějaký vnější podnět nebo událost (která může nastat asynchronně ), spíše než provádění in-line prohlášení o řízení toku.
Na úrovni strojového jazyka nebo jazyka sestavení pokyny toku řízení obvykle fungují změnou čítače programu . U některých centrálních procesorových jednotek (CPU) jsou jediným dostupným příkazem řídicího toku podmíněné nebo nepodmíněné větvící pokyny, označované také jako skoky.
Kategorie
Druhy příkazů toku řízení podporovaných různými jazyky se liší, ale lze je kategorizovat podle jejich účinku:
- Pokračování v jiném prohlášení (bezpodmínečná větev nebo skok)
- Provedení sady příkazů pouze v případě, že je splněna nějaká podmínka (volba - tj. Podmíněná větev )
- Provedení sady příkazů nula nebo vícekrát, dokud není splněna nějaká podmínka (tj. Smyčka - stejná jako podmíněná větev )
- Provedení sady vzdálených příkazů, po kterých se tok řízení obvykle vrací ( podprogramy , korutiny a pokračování )
- Zastavení programu, zabránění dalšímu provádění (bezpodmínečné zastavení)
Primitivů
Štítky
Štítek je explicitní jméno nebo číslo přiřazené pevné poloze uvnitř zdrojového kódu , a který může být odkazuje prohlášení řízení toku se objevují na jiném místě ve zdrojovém kódu. Štítek označuje pozici ve zdrojovém kódu a nemá žádný jiný účinek.
Čísla řádků jsou alternativou k pojmenovanému štítku používanému v některých jazycích (například BASIC ). Jsou to celá čísla umístěná na začátku každého řádku textu ve zdrojovém kódu. Jazyky, které je používají, často kladou omezení, že čísla řádků musí nabývat na hodnotě v každém následujícím řádku, ale nemusí vyžadovat, aby byla po sobě jdoucí. Například v ZÁKLADĚ:
10 LET X = 3
20 PRINT X
V jiných jazycích, jako je C a Ada , je štítek identifikátorem , obvykle se objevuje na začátku řádku a bezprostředně za ním je dvojtečka. Například v C:
Success: printf("The operation was successful.\n");
Jazyk ALGOL 60 povolil jako popisky celá čísla i identifikátory (oba spojené dvojtečkami s následujícím příkazem), ale jen málo, pokud nějaké jiné varianty ALGOL, umožňovaly celá čísla. Starší kompilátoři Fortranu povolovali pouze celá čísla jako štítky. Počínaje Fortranem-90 byly povoleny také alfanumerické štítky.
Jít do
Prohlášení goto (kombinace anglických slov go a to a podle toho se vyslovuje) je nejzákladnější formou bezpodmínečného přenosu kontroly.
Ačkoli klíčové slovo může být buď velké nebo malé v závislosti na jazyce, obvykle je napsáno jako:
goto label
Účinek příkazu goto způsobí, že dalším provedeným příkazem bude příkaz, který se objeví na (nebo bezprostředně po) uvedeném štítku.
Mnoho počítačových vědců, zejména Dijkstra, považovalo prohlášení Goto za škodlivé .
Podprogramy
Terminologie podprogramů se liší; mohou být alternativně známé jako rutiny, postupy, funkce (zejména pokud vracejí výsledky) nebo metody (zejména pokud patří do tříd nebo tříd typů ).
V padesátých letech byly počítačové paměti podle současných standardů velmi malé, takže podprogramy byly používány hlavně ke zmenšení velikosti programu. Část kódu byla napsána jednou a poté mnohokrát použita z různých jiných míst v programu.
Dnes se častěji používají podprogramy, které pomáhají vytvořit program strukturovanější, například izolací nějakého algoritmu nebo skrytím některé metody přístupu k datům. Pokud na jednom programu pracuje mnoho programátorů, podprogramy jsou jeden druh modularity, který může pomoci rozdělit práci.
Sekvence
Ve strukturovaném programování je uspořádané sekvenování po sobě jdoucích příkazů považováno za jednu ze základních řídicích struktur, která se používá jako stavební blok programů vedle iterace, rekurze a volby.
Minimální strukturovaný řídicí tok
V květnu 1966 publikovali Böhm a Jacopini v Komunikaci ACM článek, který ukázal, že jakýkoli program s goto s lze transformovat do bezobslužné formy zahrnující pouze volbu (IF THEN ELSE) a smyčky (WHILE podmínka DO xxx), event. s duplikovaným kódem a/nebo přidáním booleovských proměnných (true/false příznaky). Pozdější autoři ukázali, že volbu lze nahradit smyčkami (a ještě dalšími booleovskými proměnnými).
Že je takový minimalismus možný, neznamená, že je nezbytně žádoucí; koneckonců počítače teoreticky potřebují pouze jednu strojovou instrukci (odečte jedno číslo od druhého a větví, pokud je výsledek negativní), ale praktické počítače mají desítky nebo dokonce stovky strojních instrukcí.
Článek Böhma a Jacopiniho ukázal, že všechny programy mohou být bez přechodů. Jiný výzkum ukázal, že řídicí struktury s jedním vstupem a jedním výstupem byly mnohem snáze pochopitelné než jakákoli jiná forma, hlavně proto, že je bylo možné použít kdekoli jako příkaz bez narušení toku řízení. Jinými slovy, byly složitelné . (V této strategii pokračovaly pozdější vývojy, například nepříliš přísné programovací jazyky -a v poslední době také transakce se skládatelným softwarem -a ještě více se tak skládaly součásti programů.)
Někteří akademici zaujali puristický přístup k výsledku Böhm-Jacopini a tvrdili, že i instrukce jako break
a return
ze středu smyček jsou špatnou praxí, protože v Böhm-Jacopiniho důkazu nejsou potřeba, a proto prosazovali, aby všechny smyčky měly jeden výstupní bod. Tento puristický přístup je ztělesněn v jazyce Pascal (navrženém v letech 1968–1969), který byl až do poloviny 90. let preferovaným nástrojem pro výuku úvodního programování na akademické půdě. Přímá aplikace Böhm-Jacopiniho věty může mít za následek, že do strukturovaného grafu budou zavedeny další lokální proměnné, a také může dojít k určité duplikaci kódu . Pascal je ovlivněn oběma těmito problémy a podle empirických studií citovaných Ericem S. Robertsem měli studentští programátoři potíže s formulací správných řešení v Pascalu pro několik jednoduchých problémů, včetně psaní funkce pro vyhledávání prvku v poli. Studie Henryho Shapira z roku 1980 citovaná Robertsem zjistila, že s použitím pouze řídicích struktur poskytnutých Pascalem poskytlo správné řešení pouze 20% subjektů, zatímco žádný subjekt pro tento problém nenapsal nesprávný kód, pokud mu bylo dovoleno napsat návrat z uprostřed smyčky.
Řídicí struktury v praxi
Většina programovacích jazyků s řídicími strukturami má počáteční klíčové slovo, které označuje typ zapojené řídicí struktury. Jazyky se poté rozdělí, zda mají řídicí struktury konečné klíčové slovo.
- Žádné konečné klíčové slovo: ALGOL 60 , C , C ++ , Haskell , Java , Pascal , Perl , PHP , PL/I , Python , PowerShell . Takové jazyky potřebují nějaký způsob seskupování příkazů dohromady:
- ALGOL 60 a Pascal:
begin
...end
- C, C ++, Java, Perl, PHP a PowerShell: složené závorky
{
...}
- PL/I:
DO
...END
- Python: používá úroveň odsazení (viz pravidlo mimo stranu )
- Haskell: Lze použít buď úroveň odsazení, nebo složené závorky a lze je libovolně míchat
- Lua: používá
do
...end
- ALGOL 60 a Pascal:
- Konečné klíčové slovo: Ada , ALGOL 68 , Modula-2 , Fortran 77 , Mythryl , Visual Basic . Formy konečného klíčového slova se liší:
- Ada: konečné klíčové slovo je
end
+ mezera + počáteční klíčové slovo např.if
...end if
,loop
...end loop
- ALGOL 68, Mythryl: počáteční klíčové slovo napsané pozpátku, např.
if
...fi
,case
...esac
- Fortran 77: konečné klíčové slovo je
END
+ počáteční klíčové slovo např.IF
...ENDIF
,DO
...ENDDO
- Modula-2: stejné konečné klíčové slovo
END
pro všechno - Visual Basic: každá řídicí struktura má své vlastní klíčové slovo.
If
...End If
;For
...Next
;Do
...Loop
;While
...Wend
- Ada: konečné klíčové slovo je
Výběr
If-then- (else) prohlášení
Podmíněné výrazy a podmíněné konstrukce jsou rysy programovacího jazyka, které provádějí různé výpočty nebo akce v závislosti na tom, zda logická podmínka specifikovaná programátorem vyhodnotí jako true nebo false.
-
IF..GOTO
. Formulář nalezený v nestrukturovaných jazycích, napodobující typickou instrukci strojového kódu, by po splnění podmínky přeskočil na (GOTO) číslo štítku nebo řádku. -
IF..THEN..(ENDIF)
. Jakékoli jednoduché prohlášení nebo vnořený blok by se nemělo omezovat na skok, ale za klíčovým klíčovým slovem THEN. Toto je strukturovaná forma. -
IF..THEN..ELSE..(ENDIF)
. Jak je uvedeno výše, ale s druhou akcí, která má být provedena, pokud je podmínka nepravdivá. Jedná se o jednu z nejběžnějších forem s mnoha variacemi. Některé vyžadují terminálENDIF
, jiné ne. C a související jazyky nevyžadují klíčové slovo terminálu nebo „pak“, ale vyžadují podmínku v závorkách. - Podmíněné příkazy mohou být a často jsou vnořeny uvnitř jiných podmíněných příkazů. Některé jazyky umožňují
ELSE
aIF
lze je kombinovatELSEIF
, takže není nutné mítENDIF
na konci složeného příkazu řadu nebo jiná závěrečná prohlášení.
Pascal : | Ada : | C : | Skript prostředí : | Python : | Lisp : |
---|---|---|---|---|---|
if a > 0 then
writeln("yes")
else
writeln("no");
|
if a > 0 then
Put_Line("yes");
else
Put_Line("no");
end if;
|
if (a > 0) {
printf("yes");
}
else {
printf("no");
}
|
if [ $a -gt 0 ]; then
echo "yes"
else
echo "no"
fi
|
if a > 0:
print("yes")
else:
print("no")
|
(princ
(if (plusp a)
"yes"
"no"))
|
Mezi méně obvyklé varianty patří:
- Některé jazyky, například Fortran , mají třícestný nebo aritmetický test, pokud testují, zda je číselná hodnota kladná, záporná nebo nulová.
- Některé jazyky mají funkční formu
if
prohlášení, například Lispcond
. - Některé jazyky mají operátor formu
if
prohlášení, jako například C je ternární operátor . -
Perl doplňuje styl C
if
pomocíwhen
aunless
. -
Smalltalk používá
ifTrue
aifFalse
zprávy k implementaci podmíněných, nikoli jakýchkoli základních jazykových konstrukcí.
Případy a přepínače
Přepněte příkazy (nebo příkazy případu nebo vícecestné větve ) porovnejte danou hodnotu se zadanými konstantami a proveďte akci podle první konstanty, která se má shodovat. Pokud žádná shoda neuspěje, obvykle existuje ustanovení o provedení výchozí akce („else“, „jinak“). Příkazy přepínače mohou povolit optimalizace kompilátoru, například vyhledávací tabulky . V dynamických jazycích nemusí být případy omezeny na konstantní výrazy a mohou se rozšířit na párování vzorů , jako v příkladu skriptu prostředí vpravo, kde *)
implementuje výchozí případ jako glob odpovídající libovolnému řetězci. Case Logic může být také realizována ve funkční podobě, jako v SQL je decode
prohlášení.
Pascal : | Ada : | C : | Skript prostředí : | Lisp : |
---|---|---|---|---|
case someChar of
'a': actionOnA;
'x': actionOnX;
'y','z':actionOnYandZ;
else actionOnNoMatch;
end;
|
case someChar is
when 'a' => actionOnA;
when 'x' => actionOnX;
when 'y' | 'z' => actionOnYandZ;
when others => actionOnNoMatch;
end;
|
switch (someChar) {
case 'a': actionOnA; break;
case 'x': actionOnX; break;
case 'y':
case 'z': actionOnYandZ; break;
default: actionOnNoMatch;
}
|
case $someChar in
a) actionOnA ;;
x) actionOnX ;;
[yz]) actionOnYandZ ;;
*) actionOnNoMatch ;;
esac
|
(case some-char
((#\a) action-on-a)
((#\x) action-on-x)
((#\y #\z) action-on-y-and-z)
(else action-on-no-match))
|
Smyčky
Smyčka je posloupnost příkazů, která je zadána jednou, ale která může být provedena několikrát za sebou. Kód „uvnitř“ smyčky ( tělo smyčky, zobrazený níže jako xxx ) je dodržován stanovený počet opakování nebo jednou pro každou kolekci položek, nebo dokud není splněna nějaká podmínka, nebo na neurčito .
Ve funkčních programovacích jazycích, jako je Haskell a Scheme , lze smyčky vyjádřit pomocí rekurze nebo iterace s pevným bodem, nikoli pomocí explicitních smyčkových konstrukcí. Rekurze ocasu je speciální případ rekurze, který lze snadno transformovat na iteraci.
Počet smyček
Většina programovacích jazyků má konstrukce pro opakování smyčky několikrát. Ve většině případů může počítání jít dolů namísto nahoru a lze použít i jiné velikosti kroků než 1.
FOR I = 1 TO N | for I := 1 to N do begin xxx | xxx NEXT I | end; ------------------------------------------------------------ DO I = 1,N | for ( I=1; I<=N; ++I ) { xxx | xxx END DO | }
V těchto příkladech platí, že pokud N <1, pak se tělo smyčky může spustit jednou (přičemž I má hodnotu 1) nebo vůbec, v závislosti na programovacím jazyce.
V mnoha programovacích jazycích lze ve smyčce řízené počtem spolehlivě použít pouze celá čísla. Čísla s plovoucí desetinnou čárkou jsou reprezentována nepřesně kvůli hardwarovým omezením, takže smyčka jako
for X := 0.1 step 0.1 to 1.0 do
se může opakovat 9 nebo 10krát, v závislosti na zaokrouhlovacích chybách a/nebo verzi hardwaru a/nebo kompilátoru. Kromě toho, pokud k přírůstku X dochází opakovaným přidáváním, nahromaděné chyby zaokrouhlování mohou znamenat, že hodnota X v každé iteraci se může poměrně výrazně lišit od očekávané sekvence 0,1, 0,2, 0,3, ..., 1,0.
Smyčky řízené podmínkou
Většina programovacích jazyků má konstrukce pro opakování smyčky, dokud se nezmění některé podmínky. Některé varianty testují stav na začátku smyčky; ostatní to testují na konci. Pokud je test na začátku, tělo může být zcela vynecháno; pokud je na konci, tělo je vždy provedeno alespoň jednou.
DO WHILE (test) | repeat xxx | xxx LOOP | until test; ---------------------------------------------- while (test) { | do xxx | xxx } | while (test);
Kontrola zlomu je metoda detekce změna hodnoty použity v rámci běžných smyčky zpracování spouštěcí pro skupiny hodnot. Hodnoty jsou monitorovány ve smyčce a změna přesměruje tok programu na zpracování skupinové události s nimi spojené.
DO UNTIL (End-of-File) IF new-zipcode <> current-zipcode display_tally(current-zipcode, zipcount) current-zipcode = new-zipcode zipcount = 0 ENDIF zipcount++ LOOP
Smyčky řízené sběrem
Několik programovacích jazyků (např. Ada , D , C ++ 11 , Smalltalk , PHP , Perl , Object Pascal , Java , C# , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 a novější) má speciální konstrukce, které umožňují implicitní procházející všemi prvky pole nebo všemi členy sady nebo kolekce.
someCollection do: [:eachElement |xxx].
for Item in Collection do begin xxx end; foreach (item; myCollection) { xxx } foreach someArray { xxx } foreach ($someArray as $k => $v) { xxx } Collection<String> coll; for (String s : coll) {} foreach (string s in myStringCollection) { xxx } someCollection | ForEach-Object { $_ }
forall ( index = first:last:step... )
Scala má výrazy for-expressions , které generalizují smyčky řízené kolekcí, a také podporují další použití, například asynchronní programování . Haskell má výrazy a porozumění, které dohromady poskytují podobnou funkci jako výrazy ve Scale.
Obecná iterace
Obecně iterace konstrukty jako například C v for
prohlášení a Common Lisp je do
forma může být použita k vyjádření některého z výše uvedených druhů smyček, a jiní, jako je vytváření smyček přes určitý počet kolekcí paralelně. Tam, kde lze použít specifičtější smyčkovou konstrukci, je obvykle upřednostňována před obecnou iterační konstrukcí, protože často činí účel výrazu jasnějším.
Nekonečné smyčky
Nekonečné smyčky se používají k zajištění smyček programových segmentů navždy nebo dokud nenastanou výjimečné podmínky, jako je chyba. Například program řízený událostmi (jako je server ) by se měl navždy opakovat a zpracovávat události tak, jak nastanou, pouze se zastaví, když je proces ukončen operátorem.
Nekonečné smyčky lze implementovat pomocí jiných konstrukcí toku řízení. Nejčastěji je to u nestrukturovaného programování skok zpět (goto), zatímco u strukturovaného programování jde o neurčitou smyčku (while loop) nastavenou tak, aby nikdy nekončila, a to buď vynecháním podmínky, nebo explicitním nastavením na true, jako while (true) ...
. Některé jazyky mají speciální konstrukce pro nekonečné smyčky, obvykle vynecháním podmínky z neurčité smyčky. Mezi příklady patří Ada ( loop ... end loop
), Fortran ( DO ... END DO
), Go ( for { ... }
) a Ruby ( loop do ... end
).
Nekonečná smyčka je často neúmyslně vytvořena chybou programování ve smyčce řízené podmínkami, přičemž podmínka smyčky používá proměnné, které se ve smyčce nikdy nemění.
Pokračování další iterací
Někdy v těle smyčky existuje touha přeskočit zbytek těla smyčky a pokračovat v další iteraci smyčky. Některé jazyky poskytují prohlášení jako continue
(většina jazyků), skip
nebo next
(Perl a Ruby), které to udělají. Účinkem je předčasné ukončení nejvnitřnějšího těla smyčky a následné pokračování jako obvykle s další iterací. Pokud je iterace poslední ve smyčce, výsledkem je předčasné ukončení celé smyčky.
Znovu provést aktuální iteraci
Některé jazyky, jako Perl a Ruby, mají redo
příkaz, který restartuje aktuální iteraci od začátku.
Restartujte smyčku
Ruby má retry
příkaz, který restartuje celou smyčku od počáteční iterace.
Předčasný odchod ze smyček
Při použití smyčky řízené počtem k vyhledávání v tabulce může být žádoucí zastavit vyhledávání, jakmile je nalezena požadovaná položka. Některé programovací jazyky poskytují příkazy jako break
(většina jazyků), Exit
(Visual Basic) nebo last
(Perl), což má za následek okamžité ukončení aktuální smyčky a přenos řízení do příkazu bezprostředně po této smyčce. Dalším termínem pro smyčky s předčasným ukončením je smyčka a půl .
Následující příklad je proveden v Ada, která podporuje jak předčasné ukončení smyček, tak smyčky s testem uprostřed . Obě funkce jsou velmi podobné a porovnání obou úryvků kódu ukáže rozdíl: early exit musí být kombinován s příkazem if, zatímco podmínka uprostřed je samostatná konstrukce.
with Ada.Text IO;
with Ada.Integer Text IO;
procedure Print_Squares is
X : Integer;
begin
Read_Data : loop
Ada.Integer Text IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Print_Squares;
Python podporuje podmíněné spouštění kódu v závislosti na tom, zda byla smyčka ukončena předčasně (s break
příkazem) nebo ne pomocí klauzule else se smyčkou. Například,
for n in set_of_numbers:
if isprime(n):
print("Set contains a prime number")
break
else:
print("Set did not contain any prime numbers")
else
Klauzule ve výše uvedeném příkladu je spojeno s for
prohlášením, a nikoli vnitřní if
prohlášení. Python for
i while
smyčky podporují takovou klauzuli else, která se spouští pouze v případě, že nedojde k předčasnému ukončení smyčky.
Některé jazyky podporují vylamování vnořených smyček; v teoretických kruzích se jim říká víceúrovňové přestávky. Jedním z běžných příkladů použití je hledání vícerozměrné tabulky. To lze provést buď prostřednictvím víceúrovňových přestávek (vylomení N úrovní), jako v bash a PHP, nebo pomocí označených přestávek (vylomení a pokračování na daném štítku), jako v Javě a Perlu. Alternativy k víceúrovňovým přestávkám zahrnují jednotlivé přestávky spolu se stavovou proměnnou, která je testována, aby se vylomila další úroveň; výjimky, které jsou zachyceny na úrovni, která je rozdělena; umístění vnořených smyček do funkce a použití návratu k ukončení celé vnořené smyčky; nebo pomocí štítku a příkazu goto. C neobsahuje víceúrovňovou přestávku a obvyklou alternativou je použít goto k implementaci označené přestávky. Python nemá víceúrovňové přerušení ani pokračování - to bylo navrženo v PEP 3136 a zamítnuto na základě toho, že přidaná složitost nestála za vzácné legitimní použití.
Pojem víceúrovňových přestávek je v teoretické informatice zajímavý , protože dává vzniknout tomu, čemu se dnes říká kosarajuská hierarchie . V roce 1973 S. Rao Kosaraju upřesnil větu o strukturovaném programu tím, že dokázal, že je možné vyhnout se přidávání dalších proměnných ve strukturovaném programování, pokud jsou povoleny víceúrovňové přestávky smyček libovolné hloubky. Kosaraju dále dokázal, že existuje přísná hierarchie programů: pro každé celé číslo n existuje program obsahující víceúrovňové přerušení hloubky n, které nelze přepsat jako program s víceúrovňovými přestávkami hloubky menší než n bez zavedení přidaných proměnné.
Lze také return
vystoupit z podprogramu provádějícího smyčkové příkazy, který se vymyká jak vnořené smyčce, tak podprogramu. Existují i jiné navrhované řídicí struktury pro více přestávek, ale ty jsou obecně implementovány jako výjimky.
David Watt ve své učebnici z roku 2004 používá Tennentův pojem sekvencer k vysvětlení podobnosti mezi víceúrovňovými přestávkami a příkazy k návratu. Watt poznamenává, že třída sekvencerů známá jako únikové sekvencery , definovaná jako „sekvencer, který ukončí provádění textově uzavírajícího příkazu nebo procedury“, zahrnuje jak přestávky ze smyček (včetně víceúrovňových přestávek), tak příkazy return. Jak je však běžně implementováno, návratové sekvencery mohou také nést (návratovou) hodnotu, zatímco sekvenční sekvencer, jak je implementován v současných jazycích, obvykle nemůže.
Smyčkové varianty a invarianty
K vyjádření správnosti smyček se používají varianty smyček a invarianty smyček.
V praxi je smyčková varianta celočíselný výraz, který má počáteční nezápornou hodnotu. Hodnota varianty se musí snižovat během každé iterace smyčky, ale nikdy nesmí být záporná během správného provedení smyčky. Varianty smyček se používají k zajištění ukončení smyček.
Invariant smyčky je tvrzení, které musí být pravdivé před první iterací smyčky a zůstat pravdivé po každé iteraci. To znamená, že když smyčka skončí správně, jsou splněny podmínky ukončení i invariant smyčky. Smyčkové invarianty se používají ke sledování konkrétních vlastností smyčky během postupných iterací.
Některé programovací jazyky, například Eiffel, obsahují nativní podporu pro varianty smyček a invarianty. V ostatních případech je podpora doplňkem, jako je například specifikace Java Modeling Language pro smyčkové příkazy v Javě .
Smyčkový podjazyk
Některé dialekty Lisp poskytují rozsáhlý podjazyk pro popis smyček. Počáteční příklad lze nalézt v konverzačním Lispu Interlisp . Common Lisp poskytuje Loop makro, které implementuje takový podjazyk.
Tabulka křížových referencí systému smyček
Programovací jazyk | podmiňovací způsob | smyčka | předčasný odchod | pokračování smyčky | předělat | zkuste to znovu | správnost zařízení | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
začít | střední | konec | počet | sbírka | Všeobecné | nekonečný | varianta | invariantní | |||||
Ada | Ano | Ano | Ano | Ano | pole | Ne | Ano | hluboko vnořený | Ne | ||||
APL | Ano | Ne | Ano | Ano | Ano | Ano | Ano | hluboko vnořený | Ano | Ne | Ne | ||
C | Ano | Ne | Ano | Ne | Ne | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
C ++ | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
C# | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | ||||
COBOL | Ano | Ne | Ano | Ano | Ne | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
Lisp | Ano | Ano | Ano | Ano | pouze vestavěný | Ano | Ano | hluboko vnořený | Ne | ||||
D | Ano | Ne | Ano | Ano | Ano | Ano | Ano | hluboko vnořený | hluboko vnořený | Ne | |||
Eiffelova | Ano | Ne | Ne | Ano | Ano | Ano | Ne | jedna úroveň | Ne | Ne | Ne | pouze celé číslo | Ano |
F# | Ano | Ne | Ne | Ano | Ano | Ne | Ne | Ne | Ne | Ne | |||
FORTRAN 77 | Ano | Ne | Ne | Ano | Ne | Ne | Ne | jedna úroveň | Ano | ||||
Fortran 90 | Ano | Ne | Ne | Ano | Ne | Ne | Ano | hluboko vnořený | Ano | ||||
Fortran 95 a novější | Ano | Ne | Ne | Ano | pole | Ne | Ano | hluboko vnořený | Ano | ||||
Haskell | Ne | Ne | Ne | Ne | Ano | Ne | Ano | Ne | Ne | Ne | |||
Jáva | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | nepůvodní | nepůvodní | |
JavaScript | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
Přírodní | Ano | Ano | Ano | Ano | Ne | Ano | Ano | Ano | Ano | Ano | Ne | ||
OCaml | Ano | Ne | Ne | Ano | pole, seznamy | Ne | Ne | Ne | Ne | Ne | |||
PHP | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
Perl | Ano | Ne | Ano | Ne | Ano | Ano | Ne | hluboko vnořený | hluboko vnořený | Ano | |||
Krajta | Ano | Ne | Ne | Ne | Ano | Ne | Ne | hluboko vnořený | hluboko vnořený | Ne | |||
REBOL | Ne | Ano | Ano | Ano | Ano | Ne | Ano | jedna úroveň | Ne | Ne | |||
Rubín | Ano | Ne | Ano | Ano | Ano | Ne | Ano | hluboko vnořený | hluboko vnořený | Ano | Ano | ||
Standardní ML | Ano | Ne | Ne | Ne | pole, seznamy | Ne | Ne | Ne | Ne | Ne | |||
Visual Basic .NET | Ano | Ne | Ano | Ano | Ano | Ne | Ano | jedna úroveň pro každý typ smyčky | jedna úroveň pro každý typ smyčky | ||||
Prostředí PowerShell | Ano | Ne | Ano | Ne | Ano | Ano | Ne | ? | Ano |
-
a
while (true)
se pro tento účel nepočítá jako nekonečná smyčka, protože to není vyhrazená jazyková struktura. -
a b c d e f g h C'sloop je obecný smyčkový konstrukt, ne konkrétně počítající, i když se k tomu často používá.
for (init; test; increment)
- a b c Hluboké přestávky lze v APL, C, C ++ a C# provádět pomocí štítků a fotografií.
- iterace přes objekty bylapřidánav PHP 5.
-
a b c Počítací smyčku lze simulovat iterací přes přírůstkový seznam nebo generátor, například Python
range()
. - a b c d e Hluboké přestávky lze dosáhnout použitím zpracování výjimek.
-
a Neexistuje žádná speciální konstrukce, protože k tomu
while
lze použít funkci. - a Neexistuje žádná speciální konstrukce, ale uživatelé mohou definovat obecné funkce smyčky.
-
a StandardC ++ 11představilrozsah založený na. VSTLexistujefunkce
std::for_each
šablony,která může iterovat nakontejnerechSTLapro každý prvekvolatunární funkci. Funkčnost lzena těchto kontejnerechvytvořit také jakomakro. - Count řízené přemety provádí iteraci přes intervalu celé číslo; předčasný odchod zahrnutím další podmínky pro odchod.
-
a Eiffel podporuje vyhrazené slovo
retry
, ale používá se přizpracování výjimek, nikoli při řízení smyčky. - a Vyžadujespecifikační jazyk pro behaviorální rozhraníJava Modeling Language(JML).
- a Vyžaduje, aby varianty smyčky byly celá čísla; transfinitní varianty nejsou podporovány. [1]
- a D podporuje nekonečné kolekce a schopnost iterovat přes tyto kolekce. To nevyžaduje žádnou speciální konstrukci.
-
a Hlubokých přestávek lze dosáhnout pomocí
GO TO
postupů a postupů. - Common Lisp předchází koncept generického typu kolekce.
Strukturovaný nelokální řídicí tok
Mnoho programovacích jazyků, zejména těch, které upřednostňují dynamičtější styly programování, nabízí konstrukce pro nelokální řídicí tok . Ty způsobí, že tok provedení vyskočí z daného kontextu a obnoví se v nějakém předem deklarovaném bodě. Podmínky , výjimky a pokračování jsou tři běžné druhy nelokálních řídicích konstruktů; existují i exotičtější, jako jsou generátory , korutiny a asynchronní klíčové slovo.
Podmínky
PL/I má asi 22 standardních podmínek (např. ZERODIVIDE SUBSCRIPTRANGE ENDFILE), které lze zvýšit a které lze zachytit: ON podmínkou akce; Programátoři mohou také definovat a používat své vlastní pojmenované podmínky.
Stejně jako nestrukturovaný if lze zadat pouze jeden příkaz, takže v mnoha případech je potřeba GOTO, aby se rozhodlo, kde by měl tok řízení pokračovat.
Některé implementace měly bohužel značnou režii v prostoru i čase (zejména SUBSCRIPTRANGE), takže se mnoho programátorů pokusilo vyhnout se používání podmínek.
Běžné příklady syntaxe:
ON condition GOTO label
Výjimky
Moderní jazyky mají specializovanou strukturovanou konstrukci pro zpracování výjimek, která nespoléhá na použití GOTO
nebo (víceúrovňové) přestávky nebo návraty. Například v C ++ lze psát:
try {
xxx1 // Somewhere in here
xxx2 // use: '''throw''' someValue;
xxx3
} catch (someClass& someId) { // catch value of someClass
actionForSomeClass
} catch (someType& anotherId) { // catch value of someType
actionForSomeType
} catch (...) { // catch anything not already caught
actionForAnythingElse
}
catch
Výše lze použít libovolný počet a rozmanitost klauzulí. Pokud neexistuje catch
konkrétní shoda s konkrétním throw
, ovládací prvek se převede zpět prostřednictvím volání podprogramů a/nebo vnořených bloků, dokud catch
není nalezeno párování nebo dokud není dosaženo konce hlavního programu, kdy je program násilně zastaven pomocí vhodné chybové zprávy.
Prostřednictvím vlivu C ++ catch
je klíčové slovo vyhrazené pro deklaraci obslužné rutiny výjimky shody vzorů v jiných dnes populárních jazycích, jako je Java nebo C#. Některé další jazyky, jako je Ada, používají klíčové slovo exception
k zavedení obsluhy výjimek a pak mohou dokonce použít jiné klíčové slovo ( when
v Ada) pro porovnávání vzorů. Několik jazyků, jako je AppleScript, obsahuje zástupné symboly v syntaxi obsluhy výjimek, aby automaticky extrahovaly několik informací, když dojde k výjimce. Tento přístup je doložen níže on error
konstruktem z AppleScript:
try
set myNumber to myNumber / 0
on error e number n from f to t partial result pr
if ( e = "Can't divide by zero" ) then display dialog "You must not do that"
end try
Učebnice Davida Watta z roku 2004 také analyzuje zpracování výjimek v rámci sekvencerů (představeno v tomto článku v části o předčasných odchodech ze smyček). Watt poznamenává, že abnormální situace, obvykle ilustrovaná aritmetickými přetečeními nebo selháním vstupu/výstupu, jako soubor nebyl nalezen, je druh chyby, která „je detekována v nějaké nízkoúrovňové programové jednotce, ale [pro kterou] je obsluha přirozeněji umístěna v programové jednotce na vysoké úrovni “. Program může například obsahovat několik volání ke čtení souborů, ale akce, kterou je třeba provést, když soubor není nalezen, závisí na smyslu (účelu) příslušného souboru v programu, a proto nelze rutinu zpracování této neobvyklé situace provést. umístěný v systémovém kódu nízké úrovně. Watts dále poznamenává, že zavedení testování stavových příznaků ve volajícím, jak by to zahrnovalo strukturované programování s jedním výstupem nebo dokonce (vícevýstupové) sekvencery návratu, vede k situaci, kdy „kód aplikace má tendenci být přeplněn testy stavových příznaků“ a že "programátor může zapomenout nebo líně vynechat testování stavového příznaku. Ve skutečnosti jsou abnormální situace reprezentované stavovými příznaky ve výchozím nastavení ignorovány!" Watt poznamenává, že na rozdíl od testování stavových příznaků mají výjimky opačné výchozí chování , což způsobí ukončení programu, pokud se programátor výjimku výslovně nějakým způsobem nezabývá, případně přidáním explicitního kódu, který ji bude ignorovat. Na základě těchto argumentů Watt dochází k závěru, že skokové sekvencery nebo únikové sekvencery nejsou tak vhodné jako vyhrazený sekvencer výjimek se sémantikou diskutovanou výše.
V Object Pascal, D, Java, C#a Python finally
lze do try
konstrukce přidat klauzuli . Bez ohledu na to, jak kontrola ponechává try
kód uvnitř finally
klauzule, je zaručeno, že se spustí. To je užitečné při psaní kódu, který se po dokončení zpracování musí vzdát nákladného zdroje (například otevřeného souboru nebo připojení k databázi):
FileStream stm = null; // C# example
try
{
stm = new FileStream("logfile.txt", FileMode.Create);
return ProcessStuff(stm); // may throw an exception
}
finally
{
if (stm != null)
stm.Close();
}
Protože je tento vzor poměrně běžný, má C# speciální syntaxi:
using (var stm = new FileStream("logfile.txt", FileMode.Create))
{
return ProcessStuff(stm); // may throw an exception
}
Při opuštění using
-bloku kompilátor zaručuje, že je stm
objekt uvolněn, účinně váže proměnnou do proudu souboru a abstrahuje od vedlejších účinků inicializace a uvolnění souboru. K podobnému účinku se používá with
příkaz Pythonu a argument Rubyho bloku k File.open
.
Všechny výše uvedené jazyky definují standardní výjimky a okolnosti, za nichž jsou vyvolány. Uživatelé mohou vyvolat vlastní výjimky; ve skutečnosti C ++ umožňuje uživatelům házet a chytat téměř jakýkoli typ, včetně základních typů jako int
, zatímco jiné jazyky jako Java nejsou tak tolerantní.
Pokračování
Asynchronní
C# 5.0 zavedlo asynchronní klíčové slovo pro podporu asynchronních I/O v „přímém stylu“.
Generátory
Generátory , známé také jako semikorutiny, umožňují dočasně odevzdat řízení spotřebitelské metodě, obvykle pomocí yield
klíčového slova ( popis výtěžku ). Stejně jako klíčové slovo async podporuje programování v „přímém stylu“.
Coroutines
Coroutines jsou funkce, které si mohou navzájem poskytovat kontrolu - forma kooperativního multitaskingu bez vláken.
Coroutines lze implementovat jako knihovnu, pokud programovací jazyk poskytuje buď pokračování, nebo generátory - takže rozdíl mezi korutinami a generátory v praxi je technický detail.
Křížová reference toku nelokálního ovládání
Programovací jazyk | podmínky | výjimky | generátory/korutiny | asynchronní |
---|---|---|---|---|
Ada | Ne | Ano | ? | ? |
C | Ne | Ne | Ne | Ne |
C ++ | Ne | Ano | ano, pomocí BOOST | ? |
C# | Ne | Ano | Ano | Ano |
COBOL | Ano | Ano | Ne | Ne |
Lisp | Ano | Ne | ? | ? |
D | Ne | Ano | Ano | ? |
Eiffelova | Ne | Ano | ? | ? |
Erlang | Ne | Ano | Ano | ? |
F# | Ne | Ano | Ano | Ano |
Jít | Ne | Ano | Ano | ? |
Haskell | Ne | Ano | Ano | Ne |
Jáva | Ne | Ano | Ne | Ne |
JavaScript | ? | Ano | Ano | Ano |
Cíl-C | Ne | Ano | Ne | ? |
PHP | Ne | Ano | Ano | ? |
PL/I | Ano | Ne | Ne | Ne |
Krajta | Ne | Ano | Ano | Ano |
REBOL | Ano | Ano | Ne | ? |
Rubín | Ne | Ano | Ano | ? |
Rez | Ne | Ano | experimentální | Ano |
Scala | Ne | Ano | prostřednictvím experimentálního rozšíření | prostřednictvím experimentálního rozšíření |
Tcl | prostřednictvím stop | Ano | Ano | přes smyčku událostí |
Visual Basic .NET | Ano | Ano | Ne | ? |
Prostředí PowerShell | Ne | Ano | Ne | ? |
Navrhované řídicí struktury
Ve spoof Datamation článku v roce 1973 R. Lawrence Clark navrhl, že prohlášení GOTO by mohlo být nahrazeno příkazem COMEFROM , a poskytuje několik zábavných příkladů. COMEFROM byl implementován v jednom esoterickém programovacím jazyce s názvem INTERCAL .
Článek Donalda Knutha z roku 1974 „Structured Programming with go to Statements“, identifikuje dvě situace, které nebyly pokryty výše uvedenými řídicími strukturami, a uvedl příklady kontrolních struktur, které by tyto situace mohly zvládnout. Navzdory své užitečnosti se tyto konstrukty dosud nedostaly do běžných programovacích jazyků.
Smyčka s testem uprostřed
V roce 1972 navrhl Dahl následující :
loop loop xxx1 read(char); while test; while not atEndOfFile; xxx2 write(char); repeat; repeat;
Pokud xxx1 vynecháme, dostaneme smyčku s testem nahoře (tradiční while smyčka). Pokud xxx2 vynecháme, dostaneme smyčku s testem ve spodní části, což odpovídá smyčce do while v mnoha jazycích. Pokud je while vynechán, dostaneme nekonečnou smyčku. Konstrukce Zde si lze představit jako dělat smyčka s while kontrole ve středu. Proto tato jediná konstrukce může nahradit několik konstrukcí ve většině programovacích jazyků.
Jazyky postrádající tuto konstrukci jej obecně napodobují pomocí ekvivalentního idiomu nekonečné smyčky:
while (true) { xxx1 if (not test) break xxx2 }
Možnou variantou je povolit více než jeden během testování; ve smyčce, ale zdá se , že použití exitwhen (viz další část) tento případ lépe pokryje.
V Ada lze výše uvedenou smyčkovou konstrukci ( smyčka - zatímco - opakovat ) reprezentovat pomocí standardní nekonečné smyčky ( smyčka - koncová smyčka ), která má uprostřed klauzuli exit (nezaměňovat s příkazem exitwhen v následující části ).
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Print_Squares is
X : Integer;
begin
Read_Data : loop
Ada.Integer_Text_IO.Get(X);
exit Read_Data when X = 0;
Ada.Text IO.Put (X * X);
Ada.Text IO.New_Line;
end loop Read_Data;
end Print_Squares;
Pojmenování smyčky (jako Read_Data v tomto příkladu) je volitelné, ale umožňuje opuštění vnější smyčky několika vnořených smyček.
Několik předčasných odchodů/výstupů z vnořených smyček
To navrhl Zahn v roce 1974. Zde je představena upravená verze.
exitwhen EventA or EventB or EventC; xxx exits EventA: actionA EventB: actionB EventC: actionC endexit;
exitwhen se používá k určení událostí, které mohou nastat v rámci xxx , jejich výskyt je indikován použitím názvu události jako příkazu. Dojde -li k nějaké události, provede se příslušná akce a poté řízení přejde těsně po ukončení . Tato konstrukce poskytuje velmi jasné oddělení mezi určením, že nějaká situace platí, a akcí, která má být v dané situaci provedena.
exitwhen je koncepčně podobný zpracování výjimek a výjimky nebo podobné konstrukce se pro tento účel používají v mnoha jazycích.
Následující jednoduchý příklad zahrnuje hledání dvojrozměrné tabulky pro konkrétní položku.
exitwhen found or missing; for I := 1 to N do for J := 1 to M do if table[I,J] = target then found; missing; exits found: print ("item is in table"); missing: print ("item is not in table"); endexit;
Bezpečnostní
Jedním ze způsobů, jak zaútočit na kus softwaru, je přesměrovat tok provádění programu. K obraně proti těmto útokům se používá řada technik integrity toku řízení , včetně zásobníků , ochrany proti přetečení vyrovnávací paměti , stínových zásobníků a ověření vtable ukazatelem.
Viz také
- Obor (počítačová věda)
- Analýza toku toku
- Schéma řízení toku
- Graf řízení toku
- Kontrolní stůl
- Coroutine
- Cyklomatická složitost
- Drakonův diagram
- Vývojový diagram
- JÍT DO
- Jeroo , pomáhá naučit se řídicí struktury
- Hlavní smyčka
- Rekurze
- Plánování (výpočetní)
- Špagetový kód
- Strukturované programování
- Podprogram
- Přepínač , podmíněně mění tok řízení
Reference
Další čtení
- Hoare, CAR „Oddíl: Algoritmus 63“, „Rychlé řazení: Algoritmus 64“ a „Najít: Algoritmus 65“. Comm. ACM 4, 321-322, 1961.
externí odkazy
- Média související s řízením toku na Wikimedia Commons
- Přejít na prohlášení považováno za škodlivé
- Jazykový přínos programování bez GOTO
- „Strukturované programování s příkazy Přejít na prohlášení“ (PDF) . Archivováno z originálu (PDF) dne 2009-08-24. (2,88 MB)
- „Příručka IBM 704“ (PDF) . (31,4 MB)