Szelekciós vezérlés

Szelekciós vezérlések

Amikor véges sok rögzített művelet közül véges sok feltétel alapján választjuk ki, hogy melyik művelet kerüljön végrehajtásra, szelekciós vezérlésről beszélünk.

Attól függően, hogy a kiválasztás milyen módszerrel történik meg, különböző altípusai vannak ennek a vezérlésnek. Ezek az

  • egyszerű szelekciós vezérlés,
  • többszörös szelekciós vezérlés,
  • esetkiválasztásos szelekció,
  • illetve a föntiek kiegészítve egyébként ágakkal.

Egyszerű szelekciós vezérlés

Egyszerű szelekció esetén egyetlen feltétel és egyetlen művelet van (amely művelet persze lehet összetett).

Legyen F egy logikai kifejezés, A pedig tetszőleges művelet. Az F feltételből és az A műveletből képzett egyszerű szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Értékeljük ki az F feltételt és folytassuk a 2.) lépéssel.
  2. Ha F értéke igaz, akkor hajtsuk végre az A műveletet és fejezzük be az összetett művelet végrehajtását.
  3. Egyébként, vagyis ha F értéke hamis, akkor fejezzük be az összetett művelet végrehajtását.

A vezérlés bővíthető úgy, hogy a 3. pontban üres művelet helyett egy B műveletet hajtunk végre. Ekkor az alábbiak szerint módosíthatjuk a vezérlés megadását.

Legyen F egy logikai kifejezés, A és B pedig tetszőleges műveletek. Az F feltételből és az A és B műveletekből képzett egyszerű szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Értékeljük ki az F feltételt és folytassuk a 2.) lépéssel.
  2. Ha F értéke igaz, akkor hajtsuk végre az A műveletet és fejezzük be az összetett művelet végrehajtását.
  3. Egyébként, vagyis ha F értéke hamis, akkor hajtsuk végre a B műveletet és fejezzük be az összetett művelet végrehajtását.

A folyamatábrákon jól látszik, hogy az F feltételtől függ, hogy merre halad a vezérlés a feltételtől függően:

kep kep

A szerkezeti ábrán most is azt látjuk, hogy milyen részproblémák megoldása lesz a szelekció megoldása. A szerkezeti ábrákon a szelekciót egy téglalappal jelöljük, ami vízszintesen két részre van osztva. Ha tudjuk, a felső részbe kerül a feltétel, vagy az az elem, ami a feltételt meghatározza, a téglalaphoz csatolt műveletek pedig azok a műveletek lesznek, amelyek a feltétel(ek) bizonyos teljesülése mellett hajtódnak végre. Egyszerű szelekció esetében maga a feltétel egyértelműen beírható a téglalap felső részébe, az alsó résznél pedig jelölni tudjuk, hogy a feltétel igaz, vagy hamis értéke mellett mely alművelet végrehajtása jelenti a művelet befejezését.

kep kep

Az egyszerű szelekciót C nyelven az if utasítással valósítjuk meg. Ennek szintaxisa:

kep

A szintaxis ábrán is látszik, hogy maga az else (egyébként) ág opcionális nyelvi szinten is, ez elhagyható.

A szintaxisnak megfelelően az egyszerű szelekció C megvalósítása:

1
2
3
if(F) {
    A;
}

Ha van egyébként ág is, akkor pedig:

1
2
3
4
5
if(F) {
    A;
} else {
    B;
}

Többszörös szelekciós vezérlés

Előfordulhat, hogy egyetlen feltétel nem elegendő ahhoz, hogy eldöntsük, mit is kellene csinálni, merre kellene haladni a vezérlésnek. Ilyenkor, ha több feltételünk van több művelettel, akkor többszörös szelekcióról beszélünk.

Legyenek Fi logikai kifejezések, Ai pedig tetszőleges műveletek 1 <= i <= n-re (azaz mind feltételből, mint A műveletből van n darab). Az Fi feltételekből és Ai műveletekből képzett többszörös szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Az Fi feltételek sorban történő kiértékelésével adjunk választ a következő kérdésre: van-e olyan i (1 <= i <= n), amelyre teljesül, hogy az Fi feltétel igaz és az összes Fj (1 <= j < i) feltétel hamis? (Azaz keressük az első Fi feltételt, ami igaz.)
  2. Ha van ilyen i, akkor hajtsuk végre az Ai műveletet és fejezzük be az összetett művelet végrehajtását.
  3. Egyébként, vagyis ha minden Fi feltétel hamis, akkor fejezzük be az összetett művelet végrehajtását.

Természetesen most is ki lehet egy egyébként ággal egészíteni a vezérlést, azaz definiálhatjuk azt a feladatot, amit akkor kell végrehajtani, amikor egyik feltétel sem teljesül. Ekkor az alábbiak szerint módosítsuk a többszörös szelekció formális leírását!

Legyenek Fi logikai kifejezések, Ai és B pedig tetszőleges műveletek 1 <= i <= n-re (azaz mind feltételből, mint A műveletből van n darab). Az Fi feltételekből, Ai és B műveletekből képzett többszörös szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Az Fi feltételek sorban történő kiértékelésével adjunk választ a következő kérdésre: Van-e olyan i (1 <= i <= n), amelyre teljesül, hogy az Fi feltétel igaz és az összes Fj (1 <= j < i) feltétel hamis? (Azaz keressük az első Fi feltételt, ami igaz.)
  2. Ha van ilyen i, akkor hajtsuk végre az Ai műveletet és fejezzük be az összetett művelet végrehajtását.
  3. Egyébként, vagyis ha minden Fi feltétel hamis, akkor hajtsuk végre B-t és fejezzük be az összetett művelet végrehajtását.

A többszörös szelekció folyamatábráin is látható, hogy egész addig, amíg nem találunk igaz feltételt, folytatódnak a feltétel kiértékelések. Ha találunk igaz feltételt, akkor az ahhoz tartozó utasítás végrehajtásával fejeződik be az összetett utasítás végrehajtása, ha egyik feltétel sem igaz, illetve ha van egyébként ág, akkor annak a végrehajtásával.

kep kep

A többszörös szelekció szerkezeti ábrája a következőképpen alakul:

kep kep

Ahogy azt már az egyszerű szelekciónál is láttuk, a szelekciót egy vízszintesen felezett téglalappal jelöljük. Mivel a dobozkába nem férne el az összes feltétel, és nem is lenne utána egyértelmű, hogy melyik feltételhez melyik utasítás tartozik, így a dobozba csak egy ? kerül. A doboz alatt sorakoznak fel az egyes feltételek, és az azokhoz tartozó műveletek.

A többszörös szelekcióra tulajdonképpen tekinthetünk úgy is, mint egyszerű szelekciókra egymás után, ahol az egyébként ágakhoz tartozik mindig egy újabb egyszerű szelekció:

kep kep

A többszörös szelekció megvalósítására nincs külön C konstrukció, így az egymásba ágyazott egyszerű szelekciókkal tudjuk megvalósítani:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
if(F1) {
    A1;
} else if(F2) {
    A2;
    ...
} else if(Fn) {
    An;
} else {
    B;
}

Példa: háromszögek osztályozása

A szelekciók bemutatására tekintsük a következő példát!

  • Problémafelvetés:

    • Egy lakatosműhely egyik részlegében lemezekből vágnak ki különféle sokszögeket, köztük háromszögeket is. Többféle speciális gépük is van, amelyek bizonyos háromszögeket gyorsabban tudnak vágni, mint az "általános" vágógép. A gyártás optimalizálására a vezetőség szeretné, ha mindenféle háromszöget a neki leginkább megfelelő géppel vágnának ki. Van egy szabályos háromszögeket gyorsan vágó gép, egy-egy egyenlő szárú háromszögeket illetve derékszögű háromszögeket kicsit lassabban vágó gép, és egy általános de lassú vágógép. A háromszögeket a három oldaluk hosszával adják meg, és elképzelhetőek hibás adatok is.
    • A vezetőség szeretne egy programot, amely segít meghatározni a megfelelő (a feladathoz leggyorsabb) vágógépet azzal a kikötéssel, hogy az egyenlő szárú derékszögű háromszögek esetén a kettő megfelelő közül bármelyik gépet használhatják. Kellene tehát egy olyan program, ami megmondja, hogy milyen háromszöget határoz meg három valós szám, mint a háromszög három oldalhosszúsága?
  • Specifikáció:

    • A bemenő adat három valós szám, jelölje ezeket a, b és c.
    • A kimenő adat a három bemenő érték mint oldalhossz alapján a következő szövegek valamelyike:
      • "Nem háromszög"
      • "Szabályos háromszög"
      • "Egyenlő szárú háromszög"
      • "Egyenlő szárú derékszögű háromszög"
      • "Derékszögű háromszög"
      • "Egyéb háromszög"
  • Algoritmustervezés:

    • Az osztályozás az alábbi feltételek alapján végezhető el:
      • Valamelyik oldal nem pozitív, azaz a<=0 vagy b<=0 vagy c<=0 .
      • Nem alkotnak háromszöget, azaz ha az a oldal a leghosszabb, akkor a>=b+c.
      • Mindhárom oldal egyforma hosszúságú, azaz a==b, b==c (ebből tranzitivitás miatt már következik, hogy a==c).
      • Van legalább két egyforma oldal, azaz a==b vagy b==c vagy a==c.
      • A háromszög derékszögű, azaz ha a a leghosszabb oldal, akkor a*a = b*b+c*c
    • Látható, hogy az egyszerűbb feltételek érdekében az osztályozás előtt érdemes legnagyobb értéket valamelyik, mondjuk az a változóba átmozgatni.
    • Az osztályozás eredményét közvetlenül kiírjuk.
  • Algoritmustervezés szerkezeti ábrával:

    kep

    Az alapfeladat 3 nagyon egyszerű részfeladat egymás után (szekvenciálisan) végrehajtható feladatából áll. Ezekből a bonyolultabb feladatokat kell részletesebben kifejteni. Az adatok átrendezésének célja, hogy a legnagyobb érték az a változóban legyen. Ehhez egyszerű szelekciót kell alkalmazni, ahol leellenőrizzük, hogy a milyen relációban áll a másik két változóval:

    kep

    Maga az osztályozás egy többszörös szelekció, ahol most a feltételeket egyszerű rövidítésekkel írjuk le:

    kep

  • Megvalósítás:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/* Milyen háromszöget határoz meg három pozitiv valós szám,
 *   mint a háromszög három oldalhosszúsága?
 * 1997. Október 13.  Dévényi Károly, devenyi@inf.u-szeged.hu
 */

#include <stdio.h>

int main() {
    double a, b, c;    /* a háromszög oldalhosszúságai */
    double m;          /* munkaváltozó a cseréhez      */

    printf("Kérem a három pozitív valós számot!\n");
    scanf("%lf%lf%lf", &a, &b, &c);

                             /* a,b,c átrendezése úgy, hogy a>=b,c legyen */
    if (a < b) {                                    /* a és b átrendezése */
        m = a;
        a = b;
        b = m;
    }
    if (a < c) {                                    /* a és c átrendezése */
        m = a;
        a = c;
        c = m;
    }
                                                           /* osztályozás */
    if (b <= 0 || c <= 0) {
        printf("Nem háromszög!\n");                     /* 1. alternatíva */
    } else if (a >= b + c) {
        printf("Nem háromszög!\n");                     /* 2. alternatíva */
    } else if (a == b && b == c) {
        printf("Szabályos háromszög.\n");               /* 3. alternatíva */
    } else if (a == b || b == c || a == c) {
        if (a * a == b * b + c * c) {                   /* 4. alternatíva */
            printf("Egyenlő szárú derékszögű háromszög.\n");
        } else {
            printf("Egyenlő szárú háromszög.\n");
        }
    } else if (a * a == b * b + c * c) {
        printf("Derékszögű háromszög.\n");              /* 5. alternatíva */
    } else {
        printf("Egyéb háromszög.\n");                        /* egyébként */
    }
                                        /* vége a többszörös szelekciónak */
    return 0;
}

Mivel a háromszögek oldalhosszúsága nemcsak egész érték lehet, így a változók deklarálásakor legyenek a változók valós típusúak. A valós változók deklarációját láthatjuk a 9-10. sorokban.

Amikor ezeket a változókat szeretnénk inicializálni a scanf utasítással, akkor a formátumsztringben a %lf-et használjuk.

Valós számok esetében szó volt arról, hogy vigyázni kell az egyenlő, nem egyenlő operátorokkal. A számábrázolás pontatlansága miatt ezek nem feltétlen működnek jól, érdemes az összehasonlítást úgy megvalósítani, hogyha a két valós különbsége nem halad meg egy küszöbszámot, akkor azok egyenlőek, egyébként különbözőek.

Módosítsuk a programot ennek megfelelően!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* Milyen háromszöget határoz meg három pozitiv valós szám,
 *   mint a háromszög három oldalhosszúsága? A valós típus
 *   pontatlansága miatt toleranciával számolunk.
 * 1997. Október 13.  Dévényi Károly, devenyi@inf.u-szeged.hu
 * 2014. Február 19.  Gergely Tamás, gertom@inf.u-szeged.hu
 */

#include <stdio.h>

#define EQUALS(X,Y) ( ( ((X) > (Y)) ? ((X) - (Y)) : ((Y) - (X)) ) <= 1e-10 )

int main() {
    double a, b, c;    /* a háromszög oldalhosszúságai */
    double m;          /* munkaváltozó a cseréhez      */

    printf("Kérem a három pozitív valós számot!\n");
    scanf("%lf%lf%lf", &a, &b, &c);

                             /* a,b,c átrendezése úgy, hogy a>=b,c legyen */
    if (a < b) {                                    /* a és b átrendezése */
        m = a;
        a = b;
        b = m;
    }
    if (a < c) {                                    /* a és c átrendezése */
        m = a;
        a = c;
        c = m;
    }
                                                           /* osztályozás */
    if (c <= 0 || b <= 0) {
        printf("Nem háromszög!\n");                     /* 1. alternatíva */
    } else if (a >= b + c) {
        printf("Nem háromszög!\n");                     /* 2. alternatíva */
    } else if (EQUALS(a,b) && EQUALS(b,c) && EQUALS(a,c)) {
        printf("Szabályos háromszög.\n");               /* 3. alternatíva */
    } else if (EQUALS(a,b) || EQUALS(b,c) || EQUALS(a,c)) {
        if (EQUALS((a * a), (b * b + c * c))) {         /* 4. alternatíva */
            printf("Egyenlő szárú derékszögű háromszög.\n");
        } else {
            printf("Egyenlő szárú háromszög.\n");
        }
    } else if (EQUALS((a * a), (b * b + c * c))) {
        printf("Derékszögű háromszög.\n");              /* 5. alternatíva */
    } else {
        printf("Egyéb háromszög.\n");                        /* egyébként */
    }
                                        /* vége a többszörös szelekciónak */
    return 0;
}

A #define EQUALS(X,Y) ( ( ((X) > (Y)) ? ((X) - (Y)) : ((Y) - (X)) ) <= 1e-10 ) makró segít abban, hogy két érték különbségéről eldöntsük, hogy azok különbsége kisebb-e, mint egy nagyon pici konstans. Amikor ezt a makrót használjuk, akkor a használat helyén levő kifejezések behelyettesítődnek X és Y helyére a makró behelyettesítés során. Hogy a makrók pontosan hogyan is működnek, illetve az ebben a makróban használt kifejezés micsoda, arról a későbbiekben lesz szó.

Az if utasítások ismételt alkalmazása

Talán az eddigi példákból már feltűnt, hogy amikor egy kódot írunk, akkor bizonyos dolgok "beljebb" kezdődnek. A kód tabulálása azt a célt szolgálja, hogy a kód áttekinthetőbb legyen, látszódjék, hogy egy utasítás, vagy utasítás blokk mely másik utasítás része.

Láttuk, hogy a függvények utasításai blokkba rendeződnek, a blokkon belüli utasításokat pedig beljebb tabulálva írjuk. Ugyanígy teszünk egy szelekciós utasításunk ágaival is. Az abban levő utasításokat blokkosítjuk, a blokkon belüli utasításokat pedig beljebb tabuláljuk. Amennyiben egy összetett utasítás részutasítása egyetlen utasításból áll, akkor a blokk persze elmaradhat, de fontos, hogy a tabulálás megmaradjon. Egyes esetekben viszont jobb, ha a blokkokat sem hagyjuk el a biztonság kedvéért.

Vegyük a következő esetet, amikor két egyszerű szelekció van egymásba ágyazva:

kep

Írjuk le ezt C kóddal:

1
2
3
4
5
6
if(F1) {
    if(F2)
        A1;
    else
        A2;
}

Mivel a belső if egyetlen utasítás, így akár a zárójelezés elmaradhat. De vajon egyértelmű ebben, hogy az else melyik if-hez tartozik? Mi van, ha elcsúszik a tabulálás?

1
2
3
4
5
if(F1)
    if(F2)
        A1;
else
  A2;

Mivel a C nyelv nem érzékeny az úgynevezett whitespace, azaz nem látható karakterekre (szóköz, tabulátor, sortörés), az else ebben az esetben is a második if-hez tartozik, azaz a két kód ekvivalens. (Csak megjegyezzük: vannak olyan programozási nyelvek, ahol ez nemigaz, vagyis a kód tabulálása, formája az értelmezését is befolyásolja.) Ha azt szeretnénk, hogy az else az első if-hez tartozzon, akkor azt zárójelezéssel jelezni kell:

kep

1
2
3
4
5
6
if(F1) {
    if(F2)
        A1;
}
else
  A2;

Esetkiválasztásos szelekciós vezérlés

Ha a többszörös szelekció valamennyi feltételét átírhatjuk úgy, hogy azok elemek valamilyen halmazokba való tartozását vizsgálja, akkor esetkiválasztásos szelekcióról beszélünk.

Legyen K egy adott típusú kifejezés, és legyenek Hi-k olyan halmazok, melynek elemeinek típusa megegyezik K típusával. Legyenek továbbá Ai tetszőleges műveletek, ahol 1<=i<=n teljesül. A K szelektor kifejezésből, Hi kiválasztó halmazokból és Ai műveletekből képzett esetkiválasztásos szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Értékeljük ki a K kifejezést és folytassuk a 2.) lépéssel.
  2. Adjunk választ a következő kérdésre: Van-e olyan i (1<=i<=n), amelyre teljesül, hogy a K ∈ Hi, és K ∉ Hj, ahol (1<=j<i)?
  3. Ha van ilyen i, akkor hajtsuk végre az Ai műveletet és fejezzük be az összetett művelet végrehajtását.
  4. Egyébként, vagyis ha K nem eleme egyetlen Hi halmaznak sem, akkor fejezzük be az összetett művelet végrehajtását.

Itt is természetesen lehet egyébként águnk, amit akkor hajtunk végre, amikor egyik feltétel sem teljesül. Ekkor a 4. lépést egészítsük ki egy nemüres B művelet végrehajtásával.

Legyen K egy adott típusú kifejezés, és legyenek Hi-k olyan halmazok, melynek elemeinek típusa megegyezik K típusával. Legyenek továbbá Ai és B tetszőleges műveletek, ahol 1<=i<=n teljesül. A K szelektor kifejezésből, Hi kiválasztó halmazokból és Ai és B műveletekből képzett esetkiválasztásos szelekciós vezérlés a következő vezérlési előírást jelenti:

  1. Értékeljük ki a K kifejezést és folytassuk a 2.) lépéssel.
  2. Adjunk választ a következő kérdésre: Van-e olyan i (1<=i<=n), amelyre teljesül, hogy a K ∈ Hi, és K ∉ Hj, ahol (1<=j<i)?
  3. Ha van ilyen i, akkor hajtsuk végre az Ai műveletet és fejezzük be az összetett művelet végrehajtását.
  4. Egyébként, vagyis ha K nem eleme egyetlen Hi halmaznak sem, akkor hajtsuk végre B-t és fejezzük be az összetett művelet végrehajtását.

Az esetkiválasztásos szelekció folyamatábrái nagyon hasonlítanak a többszörös szelekció folyamatábráihoz, a különbség csupán a feltételek megadásában van, hiszen itt fontos, hogy egy adott halmazba való tartozást vizsgálunk mindig:

kep kep

Amennyiben a szelektor kifejezés, amelynek értékét egy v változóban elmentük benne van valamelyik Hi halmazban, úgy az adott esethez tartozó Ai műveletet hajtjuk végre, egyébként pedig ellenőrizzük a következő halmazba való tartozást.

A szerkezeti ábráknál szintén egy osztott téglalappal jelezzük a szelekciót, ahol a téglalap felső felében megadjuk a szelekciót meghatározó kifejezést, az egyes halmazokat pedig az átláthatóság érdekében a téglalap alatt adjuk meg, a hozzájuk tartozó műveletekkel együtt.

kep kep

A C nyelvben az esetkiválasztásos szelekciót a switch utasítással valósítjuk meg. Kis pontatlansággal ennek a szintaktikája a következő:

kep

A pontatlanság abból adódik, hogy ezen ábra alapján akár több default címkéje is lehetne az utasításnak, valamint nem derül ki ebből az ábrából, hogy egy adott konstans érték csak egy case címke után szerepelhet, illetve az is fontos, hogy a switch szelektor kifejezés valamilyen felsorolási típusú legyen (azaz nem lehet például valós típus).

A switch utasítás szelektorától csak az függ, hogy mely ponton kezdjük el végrehajtani a switch magját. Ha a végrehajtás elkezdődött, akkor onnantól kezdve az első break vagy return utasításig megy a switch utasításainak a végrehajtása. Ha ilyen nincs, akkor akár az utolsó utasításig. Ebben a fázisban már nincs annak jelentősége, hogy van-e további case vagy default címke. Vagyis ahhoz, hogy a switch utasítás az esetkiválasztásos szelekció megvalósítása legyen, szükséges, hogy az esetkiválasztásos szelekció egy ághoz tartozó utasítások után egy break segítségével megszakítsuk a switch további utasításainak (és így magának a switch utasításnak) a végrehajtását. A break helyett speciális esetekben a return utasítás is megfelelő lehet.

A switch utasítást tehát például a következő implementációval tudja az esetkiválasztásos szelekciót megvalósítani:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
switch(K) {
    case H1:
        A1;
        break;
    ...
    case Hn:
        An;
        break;
    default:
        B;
        break;
}

A kiválasztó halmazok megadása az esetkiválasztásos szelekció kritikus pontja. Algoritmusok tervezése során bármilyen effektív halmazmegadást használhatunk, azonban a tényleges megvalósításkor csak a választott programozási nyelv eszközeit alkalmazhatjuk.

A Hi halmazok elemszáma tetszőleges lehet, a case-ek után viszont csak egy-egy érték állhat. Viszont kihasználhatjuk a switch működési elvét, miszerint a címkék csak a belépési pontot határozzák meg. Ha Hi = {xi,1, xi,2, . . . , x i,ni }, akkor az adott ág lekódolható a következő kódrészlettel:

1
2
3
4
5
6
case x_i1:
case x_i2:
...
case x_ini:
   Ai;
   break;

Példa: dátum helyességének eldöntése

Az esetkiválasztásos szelekció használatára példa a következő program.

  • Problémafelvetés: A munkaügyi hatóság ellenőrzéseket tart építkezéseken, ahol a munkanaplókat is ellenőrzik. Mivel tapasztalat, hogy az ellenőrzés hírére pánikszerűen kitöltött naplókban sok rossz dátum is szerepel, szükségük lenne egy olyan programra, amely mintegy "előszűri" az adatokat, és a nyilvánvalóan hibás dátumokat jelzi.

  • Specifikáció:

    • Az input egy honap nap egész számpár által megadott dátum.
    • Az output "A dátum helyes" vagy "A dátum nem helyes" szövegek valamelyike attól függően, hogy a honap nap dátum hónap napként helyes-e vagy sem.
      • Szökőévekkel nem foglalkozunk, a február 28 napos.
      • Ha honap értéke 2, a nap értékének 1 és 28 közé kell esnie, különben a dátum nem helyes.
      • Ha honap értéke 4, 6, 9 vagy 11, a nap értékének 1 és 30 közé kell esnie, különben a dátum nem helyes.
      • Ha honap értéke 1, 3, 5, 7, 8, 10 vagy 12, a nap értékének 1 és 31 közé kell esnie, különben a dátum nem helyes.
      • Ha honap értéke más, a dátum nem helyes.

kep

Az algoritmustervezés során használtunk egy jo nevű változót, amely logikai értéket tárol. A következő megvalósításban ezt a változót int típusúként deklaráljuk. Ugyanakkor persze érdemes megjegyezni, hogy a C nyelvnek csak a \(C^{99}\) szabvány óta része a logikai (_Bool) típus (melynek értékkészlete a {0, 1} halmaz), illetve támogatja a true, false literálok alkalmazását. Amíg ezek nem voltak a nyelvben, addig is lehetett logikai értékekkel dolgozni. A műveletek eredményeként keletkező logikai hamis értéket a 0 egész érték reprezentálja, és a 0 egész érték logikai értékként értelmezve hamisat jelent. A műveletek eredményeként keletkező logikai igaz értéket az 1 egész érték reprezentálja, de bármely 0-tól különböző egész érték logikai értékként értelmezve igazat jelent. A logikai és egész értékek C-ben teljesen konvertibilisek (a fenti konverziók szerint), így logikai értékeket egész típusú változókban is tudunk tárolni (sőt, a \(C^{99}\) szabvány előtt abban kellett).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* Eldöntendő, hogy egy dátumként megadott számpár helyes dátum-e?
 * 1997. Október 4.  Dévényi Károly, devenyi@inf.u-szeged.hu
 */

#include <stdio.h>

int main() {
    int honap, nap;
    int jo;                                 /* a Boolean érték tárolására */

    printf("Kérem a dátumot (hónap, nap) !\n");
    scanf("%d%d", &honap, &nap);

    switch (honap) {
    case 2:
        jo = (1 <= nap && nap <= 28);
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        jo = (1 <= nap && nap <= 30);
        break;
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        jo = (1 <= nap && nap <= 31);
        break;
    default:
        jo = 0;
        break;
    } /* switch */
                                                             /* Kiíratás  */
    printf("A dátum ");
    if (!jo) {                                   /* Ezt másképpen szokták */
        printf("nem ");
    }
    printf("helyes.\n");
    return 0;
}

Feltételes kifejezés

Az előző példában a kiíratás kissé összetettre sikeredett, gyakorlatilag 3 printf függvény hívásával oldottuk meg egyetlen mondat kiírását:

1
2
3
4
5
printf("A dátum ");
if (!jo) {
   printf("nem ");
}
printf("helyes.\n");

Ez lerövidíthető az úgynevezett feltételes kifejezéssel így:

1
2
3
printf("A dátum");
printf(jo ? " " : " nem ");
printf("helyes.\n");

vagy még rövidebben így:

1
printf(jo ? "A dátum helyes.\n" : "A dátum nem helyes.\n");

A feltételes operátor a C nyelv egyetlen három operandusú művelete, amely már a nyelv kezdetei óta a nyelv része. Három kifejezésből áll:

1
kifejezés_1 ? kifejezés_2 : kifejezés_3

A teljes kifejezés kiértékelése során először kiértékelésre kerül a kifejezes_1 kifejezés. Amennyiben ez igaz (azaz az érték nem 0), akkor a teljes kifejezés értéke a kifejezes_2 lesz, egyébként ha hamis (azaz 0), akkor a kifejezés értéke a kifejezes_3 lesz.


Utolsó frissítés: 2020-10-13 06:15:04