Elágazás (programozás)

Az elágazás a programozásban olyan művelet, amelyet olyan esetekben használnak, amikor egy bizonyos parancssor végrehajtásának vagy nem végrehajtásának egy bizonyos feltétel teljesülésétől vagy nem teljesülésétől kell függnie. Az elágazás a strukturált programozás három alapstruktúrájának egyike (a parancsok szekvenciális végrehajtásával és a ciklussal együtt ) .

Az elágazás megvalósításának fő formái az imperatív programozási nyelvekben a feltételes operátor (operátor if) és a többértékű választási operátor (switch, case, switch). A korai alacsony szintű nyelvekben az elágazás a feltételes elágazás operátoron keresztül valósul meg .

Feltételes operátor

A feltételes operátor bizonyos parancsok végrehajtását hajtja végre, feltéve, hogy valamilyen logikai kifejezés (feltétel) az "igaz" értéket veszi fel true. A legtöbb programozási nyelvben a feltételes utasítás kulcsszóval kezdődik if(  angolul fordítva  -  „if”). A feltételes operátornak a következő formái vannak – egy és két ággal.

Ha egy feltételes utasítást egy ággal hajtunk végre if <условие> then <команды> end, akkor a feltétel kiértékelésre kerül, és ha igaz, akkor a kulcsszóig endvégrehajtott parancsok végrehajtásra kerülnek, ellenkező esetben a program végrehajtása a feltételes utasítást követő paranccsal folytatódik. Alacsony szintű nyelveken ( összeszerelők ) ez a feltételes operátor egyetlen elérhető formája. Egyes nyelveken egy speciális kulcsszót használnak az egy ágú feltételes operátorhoz (általában ez then, angolról lefordítva  -  „  az”).

Kétágú feltételes operátor végrehajtásakor if <условие> then <команды1> else <команды2> end , ha a feltétel igaz, a kulcsszó utáni parancsok , ha a feltétel thenhamis, a kulcsszó utáni parancsok végrehajtásra kerülnek else. Ha több feltételt egymás után kell ellenőrizni, lehetőség van a feltételes állítások kaszkádjára:

if 1. feltétel akkor parancsok 1 else if feltétel 2 then parancsok 2 else if feltétel 3 then parancsok 3 ... else if feltétel N + 1 akkor parancsok N + 1 else parancsok vége ;

Ebben az esetben a feltételeket szekvenciálisan ellenőrizzük, és amint igaz teljesül, a megfelelő parancskészlet végrehajtásra kerül, és a végrehajtás a feltételes utasítást követő parancsra megy. Ha egyik feltétel sem teljesül, akkor az ágból származó parancsok végrehajtásra kerülnek else.

Számos programozási nyelv tartalmaz egy speciális konstrukciót a feltételes utasítások lépcsőzéséhez, amely lehetővé teszi, hogy több elágazást valamivel kompaktabban írjon, és kevésbé hajlamos az írási hibákra:

if feltétel1 then parancsok1 elsif feltétel2 then parancsok2 elsif feltétel3 then parancsok3 ... else parancsok vége ; ennek az utasításnak a végrehajtási sorrendje pontosan megfelel az egyszerű if-then-else utasítások fenti kaszkádjának, és a különbség pusztán formális: több egymásba ágyazott feltételes utasítás helyett ez a konstrukció egyetlen egész, és tartalmaz egy további kulcsszót elsif, amelyhez másik kulcsszó szükséges. állapot maga után.

Megvalósítások

Pascal az Algol -60-tól örökölte a szintaxist , mely szerint egy feltételes operátor ágaiba csak egy parancsot lehet elhelyezni. beginEzért, hogy több parancs is elférjen, a és kulcsszópár használatával összetett utasításokba vannak csoportosítva end. Az else ág nem kötelező. beginés endcsak akkor szükségesek, ha több operátor van (például a kód egységessége miatt ). A példában a választási operátor Pascalban:

Ha feltétel akkor kezdődik utasítások ; end else begin utasítások ; vége ;

A feltételes operátor szükségességét az Algolban és a Pascalban a kezdetek óta kritizálják. A kritikusok szerint számos összetett utasítás összezavarja a programot, zavarja a normál behúzást és hibákat vált ki (ha elfelejti az összetett utasítást ott, ahol az if utasítás utolsó ágában szükséges, a fordító nem vesz észre semmit, de a program végrehajtásakor utasítások csoportjából, amelyeket ebben az ágban kell végrehajtani, a feltétel szerint csak az első kerül végrehajtásra, a többi - mindig). A nyelvek következő generációi - Algol leszármazottai megpróbáltak megszabadulni ettől a hiányosságtól. Köztük három széles körben ismert nyelv: Algol-68 , Modula-2 és Ada . Az if utasítás felépítése bennük szinte azonos, az egyes kulcsszavakig:

  • Algol-68:
ha feltétel ... fi ;
  • Modula-2
IF feltétel 1 THEN parancs 1 ELSE IF feltétel 2 THEN parancs 2 ... ELSE parancs N VÉGE ;
  • Ada
if feltétel1 then parancsok1 else if feltétel2 then parancsok2 ... else parancsokN end if ;

A "commandX" minden esetben tetszőleges számú, pontosvesszővel elválasztott utasítás. Minden esetben a feltételes utasítás összes ága, kivéve az elsőt ("akkor" ágak), nem kötelező, és átugorható. Ha nincs "egyéb" ág, és egyik feltétel sem teljesül, akkor a vezérlés átkerül a feltételes befejezés kulcsszót követő parancsra (END, FI vagy END IF).

A C és a C++ (és utánuk a Java , C# , PHP és sok más nyelv) rendelkezik egy feltételes operátorral, amely szerkezetileg hasonló a Pascalhoz. beginA különbség az, hogy a feltételt zárójelben kell írni, és a kulcsszavak helyett endzárójeleket kell használni {}:

ha ( < feltétel > ) { < operátorok > } más { < operátorok > }

A Nemerlében a legtöbb nyelvtől eltérően, ahol egy operátornak ifegy vagy két ága lehet, egy operátornak if(a szintaxis teljesen hasonló a C nyelvhez) két ága kell legyen. Az egyik ágú feltételes operátor a kulcsszóval kezdődik when, emellett a nyelvnek van még egy feltételes operátora - unless, ami "fordított mikor" - ebben a feltételes ág parancsai hamis feltétel esetén végrehajtásra kerülnek.

when ( feltétel ){ utasítások } kivéve ha ( feltétel ) { utasítások }

A Forth nyelvben a feltételes operátor alakja más, mint más nyelvekben, a postfix jelölés és a veremszervezés miatt. A feltételes operátor három szóból áll IF ELSE THEN[1] .

< feltétel > HA < kifejezés _1_ ha a _ feltétel _ igaz > ELSE < kifejezés _2_ ha a _ feltétel _ hamis > AKKOR

Itt <условие>csak a verem tetejére tolja az értéket, IFelemzi a jelzőt, és ha:

  • nem egyenlő nullával, akkor a ELSEvagy -ig terjedő kifejezések végrehajtásra kerülnek THEN;
  • ha egyenlő nullával, akkor a ELSEés közötti kifejezések végrehajtásra kerülnek THEN.

Ha hiányzik ELSE, akkor egy szelektort kapunk: a IFés közötti kifejezések THENcsak akkor hajtódnak végre, ha a jelző nem nulla.

A Fortran kezdetben csak aritmetikai IF -vel rendelkezett, amelyben a kifejezés előjelétől függően áttérés történt a három címke valamelyikére. Például a másodfokú egyenletmegoldó rutin kódjának egy része:

DN = B * B - 4 * A * C IF ( DN ) 90 , 10 , 10 10 D = SQRT ( DN ) X1 = ( - B + D ) / ( 2 * A ) X2 = ( - B - D ) / ( 2 * A )

Ezután logikai (logikai) kifejezéseket adtunk hozzá, és egy logikai IF-et egy utasítással, amelyet a GOTO értékelt ki, később - egy strukturális IF-et (több feltétellel), például:

DN = B * B - 4 * A * C _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 2 * A ) ELSE ! (nincs kód a negatív diszkriminánshoz) END IF

A Perl támogatja a többfeltételes if szerkezetet, valamint az utasításmódosítókat, amelyek az utasítás végrehajtott része után íródnak. Például a következő két példa funkcionalitásában megegyezik:

if ( $a == 0 ) { ++ $nulla_szám ; } ++ $nulla_szám , ha $a == 0 ;

Az if helyett írhat, ha nem, ami a feltételes kifejezés értékének megfordítását okozza az ellenőrzés előtt. Ugyanez a művelet, hacsak nem:

++ $nulla_szám , hacsak nem $a != 0 ;

Összetett utasításnál (blokknál) csak a szerkezeti forma megengedett, a módosító nem. Például:

if ( $a == 0 ) { ... } else if ( $a > 0 ) { ... } else { ... }

A végső kulcsszóra nincs szükség, mivel a feltételek mellett az utasításokat {…} blokkokba kell formázni.

Az elsif ágak esetében nincs megfelelője a hacsak nem.

Az Erlang két feltételes utasítást használ: if és case. Mindkettő eredményértéke megegyezik a végrehajtott ág utolsó utasításának értékével, és használható (névhez rendelhető, függvénynek átadható...), tehát nincs benne külön hármas feltételes utasítás . Az esetleírásban a mintaillesztést hajtják végre , további feltételekkel az összehasonlított értékekre, az if utasításban pedig csak feltételtesztet. A védőtesztek korlátozott számú műveletet és beépített funkciókat tesznek lehetővé.

Példa esetre (eseménybejegyzés törlése az időfából):

{ NewEBW , NewEBN } = case dict : hiba keresése ( EBN , Node ) - > { EBW , EBN }; { ok , Expiry } -> { gb_trees : delete_any ( { Expiry , Node }, EBW ), dict : erase ( Node , EBN )} end ,

Példák arra, ha:

if NeedLater -> erlang : send_after ( trunc ( 1000 * ( 1 + Eltelt )), self (), i_apply_nodes_portion ); igaz -> nop end , After2 = if %% Ha túl régen volt, azonnal ütemezze be az időtúllépést. After1 =< ? EXPIRY_STEP_MIN -> ? EXPIRY_STEP_MIN ; After1 >= ? EXPIRY_STEP_MAX -> ? EXPIRY_STEP_MAX ; igaz -> After1 end ,

Többválasztós operátor

A kapcsoló kialakítása több (két vagy több) ágból áll. A kapcsoló egy adott ágat hajt végre a kiértékelt kulcskifejezés értékétől függően. Az alapvető különbség ezen utasítás és a feltételes operátor között, hogy a végrehajtható ág kiválasztását meghatározó kifejezés nem logikai, hanem egész számot ad vissza, vagy olyan értéket, amelynek típusa egész számra önthető. Egyes nyelvek lehetővé teszik bizonyos nem egész típusú kifejezések (például szöveges karakterláncok) használatát egy kapcsolóban.

A modern szintaktikai konstrukció prototípusa a régi programozási nyelvekben használt jump utasítás volt. Ez a parancs egy választó kifejezést adott meg, amely egy egész értéket és egy címkekészletet adott vissza. A parancs végrehajtásakor a kifejezés kiértékelésre került, és értékét használták annak a címkének a számaként (a parancslistában), amelyre az áttérés történt. Ilyen konstrukciók voltak például a Fortran ("számított GOTO") és a BASIC programozási nyelvekben . A tervezés vonzó oldala a meglehetősen magas hatásfok: a kívánt ág (ugrásjelző) meghatározásához nem kell szekvenciálisan összehasonlítani a szelektor kifejezés eredményét sok értékkel, elegendő egy feltétel nélküli ugrási utasítás tömbjét tárolni. a szükséges címekkel a memóriában, így a parancs végrehajtásakor a kívánt elem közvetlenül a kifejezésértékekből kerül kiszámításra. Ebben az esetben a parancs végrehajtásának sebessége nem függ a címkék számától. A modern nyelvekben a switch utasítás megvalósítását gyakran ugrótáblaként is megvalósítják, amely feltétel nélküli ugrásparancsokból áll a megfelelő kódrészletekre. A kiértékelendő kifejezést ugrótábla eltolási értékké alakítja, amely meghatározza a végrehajtandó parancsot. Azokban a nyelvekben, ahol a szelektor kifejezésnek nem egész értéke lehet, közel sem mindig lehet közvetlenül kiértékelni a kapcsolószerkezet kívánt ágát, ezért más módszereket használnak a végrehajtás optimalizálására.

A modern magas szintű programozási nyelvekben a switch parancsnak általában neve van switch(  angolul fordítva  -  "kapcsoló") vagy case(  angol fordításban  -  "eset"). A kiszámított címkeválasztás azonban megtartható a modern, alacsony szintű programozási nyelvekben, például a Siemens által gyártott S7-300 és S7-400 programozható logikai vezérlők STL programozási nyelvének JL utasításában.

Például C-ben a parancs szintaxisa a következő:

kapcsoló ( i ) { 0. eset : case 1 : // utasítássorozat break ; 2. eset : // utasítássorozat break ; alapértelmezett : ; }

Itt az i egy kiválasztó kifejezés, amelynek egész számokat kell adnia, a végrehajtás minden ága a kulcsszóval kezdődik case, amelyet annak a kifejezésnek az értéke követ, amely alatt ezt az ágat végre kell hajtani. A C nyelv érdekessége, hogy benne a kapcsolót pontosan úgy értelmezik, mint egy számított címkére való ugrásra vonatkozó parancsot, az ágfejlécek ( case значение :) pedig a címkék szerepét töltik be. A switch utasításból való kilépéshez az ágkód befejezése után egy speciális paranccsal lehet kilépni break. Ha az ágban nincs ilyen parancs, akkor a kiválasztott ág kódjának végrehajtása után megkezdődik az azt követő kód végrehajtása. Ez a funkció használható optimalizálásra, bár okozhat nehezen fellelhető hibákat (ha a programozó véletlenül kihagy egy szünetet, a fordító nem dob hibát, hanem a program hibásan fut). Az alapértelmezett ág akkor kerül végrehajtásra, ha a többi ág egyike sem megfelelő.

A C switch parancs szintaxisát sok nyelv örökli, de szemantikája nem mindig teljesen C-szerű. Például a C# lehetővé teszi egy karakterlánctípus-választó kifejezés és a megfelelő címkék használatát.

A logikai kifejezések számításának jellemzői

A feltételes utasításokat tartalmazó programok végrehajtásának sorrendjét jelentősen befolyásolhatja a nyelvben alkalmazott feltételes kifejezések kiértékelési logikája. Ha a feltétel egy összetett logikai kifejezés, például "f(x) > 0 ÉS g(y) > 0", kétféle stratégia létezik az eredmény kiértékelésére:

  • Teljes számítás: számítsa ki az f(x), g(y) eredményt, hasonlítsa össze az eredményeket nullával, majd végezzen az eredményeken egy ÉS műveletet. Így tesz például a Visual Basic is.
  • Hiányos számítás: számítsa ki az f(x) értéket, hasonlítsa össze az értéket nullával. Ha az összehasonlítás eredménye "igaz", akkor értékelje ki a kifejezés többi részét. Ha az első feltétel hamis, akkor hagyja ki a második feltételt, beleértve a benne szereplő g(y) számítását is, mivel az „ÉS” műveletnél, ha az egyik operandus hamis, akkor az egész kifejezés nyilvánvalóan hamis.

A második lehetőség a leggyakoribb az ipari nyelveknél (különösen az Algol, Fortran, C++, C, Java, JavaScript, ECMAScript, JScript, C#, Python esetében). Ezeknek a nyelveknek szigorú szabálya van: "A logikai kifejezés mindig balról jobbra kerül kiértékelésre, és kiértékelése leáll, amint a teljes kifejezés eredménye definiálva van." Ez azt jelenti, hogy ha egy kifejezés több részfeltételből áll, kombinálva az ÉS operátorral (AND), akkor a kifejezés kiértékelése leáll, amint az egyik részfeltétel hamisnak bizonyul (mivel a „hamis ÉS bármely érték” mindig „hamis”), és fordítva, ha több részfeltételt kombinálunk az OR operátorral (OR), akkor a kiértékelés az első igaz részfeltétel után leáll, mivel ebben az esetben a teljes kifejezés igaz, függetlenül a további kiértékeléstől. Az Exclusive VAGY operátort (XOR) tartalmazó kifejezés azonban nem értékelhető ki teljesen, mivel az egyik érték nem tudja meghatározni a teljes kifejezés számításának eredményét.

Az Ada és az Erlang nyelvek különböző kulcsszavakat használnak ezekre a változatokra: a és és vagy szavak teljes értékelést jelentenek, és és akkor, vagy más (Ada), valamint az orelse (Erlang) - hiányos. Erlangban az andalse és az orelse prioritása alacsonyabb, mint az összehasonlító operátoroknak, ami elkerüli a zárójeleket az elemi kifejezések körül. A Visual Basic .NET nyelvnek hasonló kulcsszavai vannak: AndAlso és OrElse.

A részfeltételek fix kiértékelési sorrendjét (a logikai kifejezést mindig balról jobbra értékeljük) azért vezetjük be, hogy a kifejezés kiértékelési sorrendjét szabályozni tudjuk, és először azokat a feltételeket rakjuk bele, amelyeket először ki kell értékelni. A logikai kifejezések egyébként így különböznek az aritmetikai kifejezésektől, amelyeknél a legtöbb nyelvben a részkifejezések kiértékelésének sorrendjét (hacsak nem a műveletek prioritása és asszociativitása határozza meg) nem határozza meg a nyelv, és eltérő lehet a nyelvben. különböző esetek.

Ennek a végrehajtási logikának a választása annak a ténynek köszönhető, hogy lehetővé teszi a függő elemeket használó logikai kifejezések egyszerűsítését. A klasszikus példa egy lineáris keresés egy tömbben:

// Keresés egész számok tömbjében Pascal nyelven // Paraméterek - a kívánt érték és egy nyitott egész számok tömbje // Eredmény - a talált elem indexe vagy -1, ha az elem nem található függvény Find ( e : integer ; var a : egész szám tömbje ) : integer ; var i : integer ; kezd i := 0 ; while ( i <= Magas ( a )) ÉS ( a [ i ] <> e ) do inc ( i ) ; // !!! if i <= Magas ( a ) then Find := i else Find := - 1 ; vége ;

A program által megvalósított algoritmus elég kézenfekvő, de van egy finomság a megvalósításban (lásd a felkiáltójelekkel jelölt sort): a ciklusfeltétel két részből áll, amelyeket az ÉS operátor köt össze. Az első részfeltétel azt ellenőrzi, hogy az i index túllép-e a tömbön, a második pedig azt, hogy a tömb aktuális eleme nem egyenlő-e a kívánt értékkel. Ha a tömb nem tartalmazza a kívánt értéket, akkor az utolsó elem ellenőrzése után az i változó értéke eggyel nő; a következő iterációnál az első részfeltétel hamis lesz, és a ciklus a második részfeltétel ellenőrzése nélkül ér véget. Ha a logikai kifejezések teljesen ki lettek értékelve, akkor ha az utolsó iteráció után nem volt elem a tömbben, hiba történne: az a[i] meghatározására tett kísérlet helytelen memóriaelérést okozna.

Megjegyzendő, hogy a kifejezés értékének hiányos kiértékelése mellett itt jelentős szerepe van a részfeltételek fix kiértékelési sorrendjének is: mivel a határokon kívüli ellenőrzés tömböt írják először, ez mindig a kívánt érték elérésének ellenőrzése előtt. Ha a részkifejezések kiértékelési sorrendje nem lenne definiálva, lehetetlen lenne garantálni az adott programrészlet helyességét.

A logikai kifejezések teljes kiszámításával a fenti algoritmust hozzávetőlegesen a következő formában kellene megírni:

// Keresés egész számok tömbjében Pascal nyelven // Hipotetikus változat a logikai kifejezések teljes kiértékelésével // Paraméterek - a kívánt érték és egy nyitott egész számok tömbje // Eredmény - a talált elem indexe vagy -1, ha az elem nem található függvény Keresés ( e : integer var a : egész szám tömbje ) : integer ; _ var i : integer ; f : logikai érték ; // további változó - hurok befejező jelzője begin i := 0 ; f := false ; míg nem f do if i > High ( a ) then f := igaz else if a [ i ] = e then f := igaz else inc ( i ) ; if i <= Magas ( a ) then Find := i else Find := - 1 ; vége ;

Mint látható, mesterségesen kellett beállítani a kilépési feltételek kiszámításának sorrendjét, és be kellett vezetnünk egy további változót. Az ilyen trükkök elkerülése érdekében bevezetik a logikai kifejezések hiányos kiértékelését.

Megjegyzés: A fenti kód egy példa az IF utasítás használatára, de nem több. Ez a kód nem használható szabályként Pascal-beli algoritmusok írásához.

Az optimális algoritmus egy szám megtalálásához egy tömbben a következő:

függvény Keresés ( e : integer ; var a : egész számok tömbje ) : egész szám ; var i : integer ; kezdődik Eredmény := - 1 ; for i := Alacsony ( a ) - Magas ( a ) akkor kezdődik , ha a [ i ] = e , majd kezdődik Eredmény := i ; szünet ; vége ; vége ; vége ;

Jegyzetek

  1. A Forthnak van egy operátora <условие> ?DUP <выражение>, amely megkettőzi a kifejezést, ha egy feltétel igaz