3. fejezet

Terelgetjük a számítógépet; a program futásának menete. Struktúrált programozás, építkezés Matrjoska babákból. Elágazások és logikai kifejezések. Megdolgoztatjuk a gépet: a ciklusok.

A számítógép, mint mondtam, akár százmillió vagy újabban akár egymilliárd utasítást is képes végrehajtani másodpercenként. A Jáva ugyan nagyban lassít rajta, azonban egy modern számítógép még a Jáva bájtkódot is jócskán másodpercenként tízmillió utasítás felett hajtja végre. Ekkora sebességgel száguldó vonatot terelgetni kell, nehogy valami falnak ütközzön. Ezt a feladatot látják el a programvezérlõ utasítások.

A programvezérlõ utasításokkal feltétellel vagy feltétel nélkül a program egy másik pontjára adhatjuk a vezérlést; a feltétel valamilyen kifejezés kiértékelésébõl adódik. Klasszikusan ezt a C vagy a BASIC goto utasításával egyszerû bemutatni, ez az utasítás egy paramétert kap, amely paraméter egy helyet határoz meg a programban (egy sorszámot vagy egy címkét pédául). Amikor a goto végrehajtódik, a program futása a paraméter által meghatározott helyen folytatódik.

Jávában nincsen goto és ez egy vallás elterjedésének köszönhetõ. A strukturált programozás papjai a 70-es évek elején kezdték terjeszteni, hogy nem szép dolog a programot teleszórni goto-val, mert a végrehajtás nagyban követhetetlen lesz. Ez így is van. Az egyedül üdvözítõ megoldás a vallás hívei szerint az egymást teljesen magába foglaló, struktúrált programblokkok rendszere. Egy ilyen programblokk egy vagy több (vagy sok) Jáva utasítást foglal magába és késõbb fogjuk meglátni, mit lehet velük csinálni. Elõzetesként: feltételtõl függõen végre lehet hajtani a blokkot (vagyis a benne levõ utasításokat), ismételni lehet a blokkot, egy feltételtõl függõen újra végre lehet hajtani, stb. Akárhogy is, struktúrált a program attól lesz, hogy a blokkok határai nem keresztezik egymást, a fentebb levõ blokk teljesen magában foglalja a benne levõ blokkot vagy blokkokat. A Jávában a programblokkot egy { jel vezeti be és egy } jel zárja. Megjegyzendõ, hogy egy utasítás önmagában blokkot képez, tehát ha a kapcsos zárójelek elmaradnak, a blokk csupán egy utasításból áll. Természetesen nem hiba egy utasítást kapcsos zárójelek közé fogni, csak nem szükséges. Legegyszerûbb ezt egy példával megvilágítani!

Az if utasításnak van egy paramétere. A paraméter kiértékelésétõl függõen a mögötte levõ blokkot végrehajtja vagy sem. Példa:

if( x < 0 ) {
  System.out.println( "x kisebb volt, mint 0!" );
}

Minthogy a blokk csak egy utasításból áll, a kapcsos zárójelek elhagyhatók.

if( x < 0 )
  System.out.println( "x kisebb volt, mint 0!" );

A paraméter egy új típusú, logikai kifejezés. Ha összeadunk két egész számot, az eredmény egy újabb egész lesz. Ha összehasonlítasz két számot, az eredmény logikai érték lesz. Ez egy teljesen szokásos adattípus, a neve boolean és összesen két értéke lehet, true és false. Mondhatod tehát ezt:

boolean f;
f = true;

Sõt mondhatod ezt:

int x,y;
boolean f;
x = 0; y = 1;
f = x < y;

A logikai kifejezések és operátorok tehát pont úgy viselkednek mint bármilyen kifejezés és értékül is adhatók logikai típusú változóknak. A következõ logikai mûveletek vannak két egész vagy lebegõpontos kifejezés között.

  • x < y : true az eredmény, ha x kisebb, mint y, különben false
  • x > y : true az eredmény, ha x nagyobb, mint y, különben false
  • x == y : true az eredmény, ha x egyenlõ y-nal
  • x != y : true az eredmény, ha x nem egyenlõ y-nal
  • x <= y : true ha x kisebb vagy egyenlõ y-nal
  • x >= y : true, ha x nagyobb vagy egyenlõ y-nal
  • Vannak operátorok, amik már eleve logikai értékeken dolgoznak. Ezek:

  • x && y : true akkor és csak akkor ha x true és y is true
  • x || y : true akkor, ha x vagy y közül valamelyik, esetleg mindkettõ true
  • !x : true, ha x false és viszont.
  • Nézzünk akkor egy jópofa logikai kifejezést feltételezve, hogy van x és y egész típusú változónk:

    ( ( x > -5 ) && ( x < 5 ) ) || ( ( y > -10 ) && ( y < 10 ) )

    Ez a kifejezés akkor ad true értéket, ha x -5 és 5 közé esik vagy y 10 és -10 közé esik, esetleg mindkettõ igaz.

    De térjünk vissza oda, ahonnan elindultunk, az if-hez. Az if elsõ paramétere egy logikai kifejezés, második egy programblokk, amit a kifejezés értékétõl függõen végre kell hajtani vagy átugrani. Lássunk egy praktikus példát és írjunk egy programot, ami kiírja, ha a program paramétere negatív. Ilyet még nem láttunk és most nem is fogok belemenni a részletes magyarázatába, legyen elég annyi, hogy át lehet venni a paramétereket, amivel a programot a parancssorban elindították.

    public class Negativ {
      public void prog( String args[] ) {
        
    int x;
        
    try {
          x = Integer.parseInt( args[0] );
        } catch( NumberFormatException ex ) {
          System.out.println( args[0]+" nem kiertekelheto" );
          return;
        }
        
    if( x < 0 ) {
          System.out.println( x+" negativ" );
        }
      }

      public static void main( String args[] ) {
        Negativ e = new Negativ();
        e.prog( args );
      }
    }

    Ezt a programot, mint írtam, parancssori paraméterrel kell futtatni, különben hibaüzenetet kapsz (próbáld ki!). Próbáld ki továbbá különbözõ paraméterekkel.

    java Negativ 5
    java Negativ -10
    java Negativ 0
    java Negativ -29.34
    java Negativ 2.3e12

    Az utolsó két paraméter hatására hibaüzenetet kapunk. Ennek oka, hogy a számot egészként értékeltük ki és a két utolsó paraméterrel ezt nem lehet megtenni.

    Programunknak van egy barátságtalan vonása: ha pozitív számot vagy 0-t kap, nem ír ki semmit. Szüntessük ezt meg! Megoldhatnánk a feladatot úgy, hogy a két kiírást két if-hez kötnénk pl. így:

    if( x < 0 ) {
      System.out.println( "Negativ" );
    }
    if( x >= 0 ) {
      System.out.println( "Pozitiv vagy 0" );
    }

    Nem szép, ugye? Nehéz is karbantartani, ha netán megváltoztatnánk a feltételt, a másikat sem szabad elfelejtenünk megváltoztatni. Sokkal szebben megoldható a feladat az if-hez opcionálisan kapcsolható else-ág segítségével. Ha az if utasításnak van else ága, az else mögött levõ blokk akkor hajtódik végre, ha az if feltétele hamis volt. Lássuk az elõzõ példát!

    if( x < 0 ) {
      System.out.println( "Negativ" );
    } else {
      System.out.println( "Pozitiv vagy 0" );
    }

    Feladat

    Írj egy olyan programot, ami egész paraméterérõl kiírja, pozitív, negatív, vagy nulla-e. Itt a megoldás ellenõrzésképpen.

    Ha emlékszel a 2. fejezetre, volt ott egy olyan feladatunk, ahol 2 elsõ 8 hatványát írattuk ki. Most mi lenne, ha az elsõ 16 hatványra volnánk kiváncsiak? Abban a példaprogramban egy újabb hatványhoz egyszerûen megismételtük a szükséges sorokat. Ha a számítógépet el tudjuk terelni az egyenes vonalú végrehajtástól, akkor megspórolhatunk jó sok gépelést, ha megismételtetjük vele a programblokkot. Ezt Jávában is meg tudjuk csinálni a while utasítás segítségével. A while paramétere egy logikai kifejezés és ha ez a kifejezés igaz, a while végrehajtja a mögötte levõ programblokkot. Ezek után a gép újra kiértékeli a kifejezést, újra végrehajtja a blokkot és ez így megy addig, amíg a feltétel hamissá nem válik. Ha a feltétel már a legelsõ végrehajtásnál hamis volt, a programblokk egyszer sem hajtódik végre. A feltétel vizsgálata tehát a programblokk - a ciklusmag - elõtt van, ezért ezt a ciklust elöltesztelõnek hívjuk. A következõ kis példaprogram kiírja a számokat 1-tõl 20-ig.

    int n;
    n = 1;
    while( n <= 20 ) {
      System.out.println( n );
      n = n+ 1;
    }

    Feladat

    Csinálj a fenti kis programrészletbõl végrehajtható programot, tehát tedd köré mindazt, amitõl Jáva programmá válik. Ellenõrizd a megoldást itt.

    Feladat

    Most csináld meg azt a programot, ami 2 elsõ 16 hatványát. Használd fel a 2. fejezet programját, ami az elsõ 8 hatványt írta ki a számító rutin ismételgetésével és szervezd ciklusba, amit akkor egyszerû kódismétléssel oldottunk meg. A megoldást itt ellenõrizheted.

    A ciklusok remek dolgok, egy rendes program tartalmaz belõlük jócskán. A while ciklusokkal minden feladat megoldható, de nem mindig áll kézre. A while-nak mindenekelõtt van egy hátultesztelõ változata, a do-while. Ez ugyanazt tudja, mint a while, azzal a különbséggel, hogy az ellenõrzés a ciklus végén történik, tehát a ciklusmag egyszer mindenképpen végrehajtódik. Példa:

    int n;
    n = 1;
    do {
      System.out.println( n );
      n = n+1;
    } while( n <= 20 );

    A másik fontos szerkezet a fentiekben bemutatott szerkezet zanzásított változata, amivel sok írásmunka megspórolható. A for ciklus magába foglalja a ciklusváltozó inicializálását, a ciklusfeltétel ellenõrzését (elõltesztelõ formában) és a ciklusváltozó megváltoztatását. Az általános forma a következõ:

    for( ciklusváltozó inicializálás ; feltétel ellenõrzés ; ciklusváltozó módosítás )
                                                                       ciklusmag

    Elõzõ számolós példánkat a következõképpen tudod for ciklusba átírni.

    int n;
    for( n = 0 ; n <= 20 ; n = n + 1 )
       System.out.println( n );

    Fontos megjegyezni, hogy a for fejének mindhárom része opcionális, ha elmarad, akkor a ciklusutasítás maga nem végzi el az adott mûveletet. Ez lehetõvé teszi számunkra, hogy pl. a ciklusváltozó növelését a ciklus belsejében hajtsuk végre, ha az olyan bonyolult, hogy egy utasításban nehéz leírni. Ad abszurdum a következõ program értelmes:

    for( ; ; )
      System.out.println( "Hello!" );

    Ez a programrész végtelen ciklusban fogja az üzenetet kiírni. Nincs inicializálás és nincs ciklusváltozó-növelés valamint a ciklusfeltétel elmaradása azt jelenti, hogy a feltételt nem kell ellenõrizni, hanem feltétel nélkül folytatni a ciklust.

    Feladat

    Elõzõ feladatunkat a 2 elsõ 16 hatványáról írd át for ciklusos változatba! Ha megírtad és mûködik, ellenõrizd a megoldást itt!

    Feladat

    Írj egy programot, ami kinyomtatja a kisegyszeregyet! Használd fel a System.out.println rokonát, a System.out.print-et, ami nem tesz soremelést a kinyomtatott sor végére, így több eredményt nyomtathatsz egy sorba. A szorzótábla kinyomtatásához használj két for hurkot egymás hasában, a belsõ hurok nyomtat egy sort, a külsõ ciklus ismételgeti a sornyomtató ciklust. Küzdj meg a programmal, majd ellenõrizd az eredményt! Figyeld meg, a külsõ ciklus magját kapcsos zárójelek fogják közre, mert az két utasításból áll, a belsõ for-ból és az üres szöveget (plusz egy soremelést) nyomtató System.out.println-bõl. A belsõ ciklus magja csak egy utasítást, a System.out.print-et tartalmaz, így ott a kapcsos zárójelek nem szükségesek (bár ottlétük nem lenne hiba). Figyeld meg a struktúrált programozást: a külsõ ciklus teljesen magába foglalja a belsõt!

    Ellenõrzõ kérdések

    1. Mit jelent a programblokk?
    2. Mit jelent, hogy a programblokkok struktúráltak?
    3. Hogyan kell egy programblokkot leírni Jávában?
    4. Programblokkot alkothat-e egy Jáva utasítás?
    5. Mit jelent a logikai kifejezés?
    6. Hogyan kell logikai típusú változókat deklarálni?
    7. Mit csinál az != operátor?
    8. Mit csinál a || operátor?
    9. Mi az if utasítás
    10. Mit jelent, ha az if utasításnak else ága van?
    11. Mi a while utasítás?
    12. Mi a ciklusmag?
    13. Mit jelent, hogy a while elöltesztelõ ciklus?
    14. Hogyan lehet hátultesztelõ while ciklust írni?
    15. Mi a for ciklus?
    16. Elöl- vagy hátultesztelõ a for ciklus?
    17. Mit jelent az egymásba ágyazott ciklus?

    Kovetkezo lecke Tartalomjegyzek