2. gondolatok a pascal nyelvről 2.1. A programokról általában A számítógép egy nagyon hasznos segédeszköz lehet az élet szinte minden területén. Az viszont, hogy milyen hasznos, nagyon erősen függ attól, hogy milyen programmal (szoftver) rendelkezünk, amellyel a számítógépet használjuk. Önmagában a számítógép semmire sem képes, ugyanis csak azt tudja végrehajtani, amire utasítjuk, ezt azonban nagyon pontosan és gyorsan gondolkodás nélkül végzi el. A gyors és pontos munkához cserébe viszont nekünk is pontosnak kell lennünk. Tudomásul kell vennünk, hogy a számítógép mit ért meg, és hogyan kell leírnunk, hogy azt a mi elgondolásunk szerint hajtsa végre. A számítógép csak parancsokat hajt végre, vagy programokat futtat. Ebben a bevezető fejezetben röviden és olvasmányosan ismerkedik meg a Pascal programozási nyelvvel kis program példákon keresztül az az olvasó, aki még semmilyen programozási nyelvet nem ismert ezideig. Azonban ajánljuk azoknak is, akik második nyelvként a Pascalt választották és ebben a fejezetben egy átfogó képet kapnak a nyelvről. A további fejezetek részletesen foglalkoznak a szükséges elméleti kérdé-sekkel, példákon keresztül mutatják be az utasítások használatát. A függe-lékben adjuk meg a gyakorlatként kiadott feladatok megoldását. A könyvben hivatkozott összes program megtalálható a lemezmellékleten. 2.2. A Pascal nyelvről röviden Minden program utasításokból áll, amelyik megmondja pontosan, hogy mi is történjen. Ha egy programot kezdünk írni, akkor pontos szabályok állnak a rendelkezésünkre, hogy milyen utasításokat használhatunk, azokat pontosan hogyan kell leírni. Például, ha egy Pascal programban ki akarjuk írni a képernyőre, hogy "Rendben" akkor ezt a következő formában kell megtennünk: Write('Rendben'); A Write eljárás szolgál arra, hogy az általunk kívánt szöveg a képernyőn jelenjen meg. Minden utasításnak megvan a maga szintaktikája. Nézzük meg, hogy a Write eljárást ebben az esetben hogyan kell használni. A Write után közvetlenül egy zárójelnek kell következnie, és a zárójelben aposztróf jelek között legyen a szöveg, amit ki akarunk írni, majd bezárjuk a zárójelet. Az utasítást pontosvessző zárja. Ha ebben az utasításban bármit másképpen csinálunk, például hibásan írjuk le a Write kulcsszavat, vagy elhagyunk egy zárójelet vagy egy aposztrófot, akkor a fordítóprogram hibát fog jelezni és a programot nem tudjuk futtatni, míg a jelzett hibát ki nem javítjuk. Ha csak a szöveget gépeltük be rosszul, akkor természetesen a program lefut, de az elgépelt szöveg fog megjelenni. A másik olyan fogalom amelyikkel minden programban találkozunk, az a változók fogalma. A változó fogalmát egy példán keresztül lehet talán a legjobban megérteni. Ha két számot össze akarunk adni, ahhoz a programban olyan utasítást kell írni, hogy a két szám összegét számítsa ki, azaz az egyik számhoz adja hozzá a másik számot. Egy Pascal programban ezt egy értékadó utasítással végezhetjük el, amely a következőképpen néz ki: osszeg:=egyik+masik; Ebben a sorban három változó szerepel, rendre az osszeg, az egyik és a masik. Minden változónak van egy neve (ezt a változó azonosítójának szokták nevezni), jellemzi a típusa (ez lehet például egész szám, valós szám, szöveg stb.), és a típustól függően tartalmaz egy értéket (például az összeg értéke lehet 5 vagy 319.43 is). 2.3. A Pascal program szerkezete Maga a Pascal program három egymástól jól elkülöníthető részből áll. Ezeknek a részeknek a neve a következő: - programfej - definíciós illetve deklarációs rész - programtörzs Az egyszerűbb megértés kedvéért hátulról visszafelé nézzük meg, hogy melyik rész mire szolgál. A programtörzs tulajdonképpen a program legérdekesebb része. Ebben a részben írjuk le a program utasításait, azaz itt mondjuk meg, hogy a programunk mit hajtson végre, valamint azt is, hogy milyen módon. A definíciós ill. a deklarációs részben mondjuk meg a fordítóprogramnak, hogy milyen változókat fogunk használni, illetve itt írjuk le az úgynevezett eljárásokat és függvényeket is. A programfej a Pascal programokban csak egy sor, amelyik a program kulcsszóval kezdődik, és utána a program azonosítója következik. 2.4. Az első Pascal program Most nézzük meg a fenti fogalmakat néhány igen egyszerű program segítségével. Az első program kizárólag példaként szolgál egy legrövidebb Pascal programra, amelyik kiírja a képernyőre, hogy "Rendben". A program olyan egyszerű, hogy még a deklarációs rész is hiányzik belőle. Ajánlatos a mintaprogramok kipróbálása céljából egy alkönyvtárat létrehozni. Az md (make directory) parancs hozza létre az alkönyvtárat, melynek a neve 8 karakternél nem lehet hosszabb. Legyen a neve proba: C:\>md proba Lépjünk be az alkönyvtárba a cd (change directory) paranccsal: C:\>cd proba C:\PROBA>_ Bent vagyunk a PROBA alkönyvtárban, a villogó kurzor a további parancsok megadását várja. Mivel a Turbo Pascal kényelmes fejlesztői környezetében programozunk indítsuk el a turbo programot: C:\PROBA>turbo A turbo indításával beléptünk a Turbo Pascal integrált környezetébe, ekkor a menürendszeren keresztül elérhető minden szükséges eszköz: a szövegszerkesztő a program írásához, a Pascal fordító, a futtató és a hibakereső program. Az F3 funkció gomb megnyomására a Load file név ablakban a programunk számára adunk egy file nevet. A szövegszerkesztő a megadott névvel és .PAS kiterjesztéssel elhelyezi abban az alkönyvtárban, ahonnan a turbo programot indítottuk el, kivéve, ha nem váltottunk könyvtárat a menüből. Legyen az első programunk neve legelso és Enter-t ütve a legelso.pas file számára a szerkesztőbe kerülünk és a villogó kurzor mutatja a bal felső sarokban a programunk első karakterének helyét és várja, hogy gépeljünk. program legelso; begin Write('Rendben'); end. Ha a program begépelésével készen vagyunk, akkor a Save almenüvel vagy az F2 funkció gomb leütésével a kijelölt nevű, példánkban a legelso.pas néven a program tárolásra kerül a lemezen. A program rövid ugyan, de néhány dolgot jól be lehet rajta mutatni. Egyik fontos dolog, hogy az utasítások majdnem kivétel nélkül pontosvesszővel végződnek. A másik, amiről idáig nem volt szó, az a begin és end utasítások. Ezek az ún. utasítás zárójelek, a tulajdonképpeni feladatuk az, hogy utasítások csoportjait egybefogják. A példa nem erre szolgált, hiszen egy utasítást nem lehet igazán összefogni, azonban a Pascalban a főprogram egy blokk, amely mindig begin-nel kezdődik és a program utolsó utasítása mindig az end., amelyet az általánostól eltérően nem pontosvessző, hanem pont zár le. A program fordítása és futtatása a Run menüben történik. Ha a programot már begépeltük, a fordítást és a futtatást kétféleképpen is végrehajthatjuk, vagy az F10 funkció gombbal fellépünk a főmenübe és kiválasztjuk a Run almenüpontot, vagy a Ctrl F9 ún. forró gombbal (a két billentyűt együtt kell lenyomni úgy, hogy a Ctrl gomb nyomvatartása mellett üssük le az F9-et) mindezt azonnal végrehajthatjuk. Ha a fordítóprogram hibát talál, akkor ezt a hibakódjával szövegesen is jelzi. Ilyen esetben a program utasításait újra gondosan vizsgáljuk át, javítsuk ki a megtalált hibát és újra adjuk ki a Ctrl F9 parancsot. Ha a fordító hibátlannak találta a programot, akkor azt az integrált keretrendszer le is futtatja. A program eredménye a felhasználói (ún. DOS ) ablakban jelenik meg. Ez az ablakváltás olyan gyors, hogy szemmel nem lehet követni. A program futása után ismét a szövegszerkesztőben találjuk magunkat. Ez jó jel, hiszen a program lefutott, csak még az eredményt nem láttuk. A felhasználói ablakot, tehát ahová az eredmény íródott az ALT F5 gombok egyidejű lenyomásával nézhetjük meg és bármely billentyű leütésével visszatérhetünk a Turbo Pascal integrált környezetébe és folytathatjuk a programunk bővítését, módosítását, vagy új program írását. Ha a program eredménye elfér egy képernyőn, érdemes beállítani az Output ablakot. Ennek a beállítása a következő: az F6 funkció billentyűvel átlépünk a Watch ablakba, majd az ALT F6 együttes megnyomásával beállítjuk a Watch ablak helyén az Output ablakot. Œgy a program szövege alatt kényelmesen kiértékelhető a lefuttatott program. Az Output ablakot is görgethetjünk a kurzor nyíl billentyűkkel. Az Edit és az Output ablak közötti oda-/visszaváltás az F6 billentyűvel történik (Turbo Pascal 5.0 és 5.5 verzióknál). 2.4.1. A változók deklarálása A következő program csak annyiban térjen el az előzőtől, hogy a szöveget egy szoveg nevű változóba írjuk: Program masodik; var szoveg :string; begin szoveg:='Rendben'; Write(szoveg); end. Itt először nézzük meg valamivel részletesebben a program második sorát. Ez az úgynevezett változódeklaráció. A változódeklaráció arra szolgál, hogy megmondjuk a programnak, hogy milyen változókat fogunk használni. A Pascalban minden változót deklarálni kell. A deklaráció mindig a var kulcsszóval kezdődik, ezt követi a változó azonosítója, majd a változóazonosítótól kettősponttal elválasztva következik a változó típusa, végül pedig az utasítászáró pontosvessző. Itt az alkalom, hogy megnézzük a leglényegesebb egyszerű változótípusokat. (Azért csak ezeket, mert a Pascal igen gazdag lehetőségeket kínál a változók deklarációjára, viszont az érthetőség kedvéért jobb mindent a maga idejében megemlíteni.) Az öt alapvető változótípus a következő: - integer: egész szám -32768..32767 között - real: valós szám 2.939*10-39..1.701*1038 között - char: egy karakter (pl. betű vagy valamilyen jel az ASCII kódtábla szerint.) - string: egy legfeljebb 255 karakter hosszúságú idézet - boolean: logikai változó, amelynek az értéke csak igaz (ezt true-nak írjuk) vagy hamis (false) lehet; A másik újdonság ebben a programban a program negyedik sorában szereplő úgynevezett értékadó utasítás. Általában ezzel az utasítással adunk egy adott nevű változónak értéket. Az ilyen utasításoknak a bal oldalán mindig egy változó neve szerepel, ez lesz az a változó, amelynek az értéket adunk. Ezt a nevet a := jel követi. Az értékadásban ez a két jel összetartozik és mindig ezt kell használni. Ezután következik az az érték, ami a változó új értéke lesz. Példánkban tehát a szoveg nevű változó értéke 'Rendben' lesz. Ennél az értékadásnál meg kell még jegyezni, hogy ha string típusú változó értékét mindig '' jelek közé kell tennünk, ugyanis különben a program változónévnek veszi azt, és például a szoveg:=Rendben; programsor esetén, a szoveg nevű változó értékét meg akarja változtatni úgy, hogy az a Rendben nevű változó értékével egyezzen meg, ami természetesen hiányzik a programból. Bonyolítsuk egy kicsit tovább ezt a programot: program harmadik; var i,j : integer; x : real; begin i:=9; j:=6; x:=i/j; Write(x); end. Ez a program az előzőtől először is annyiban különbözik, hogy több változót deklaráltunk. Itt lehet látni, hogy több azonos típusú változó deklarációja úgy történik, hogy vesszővel elválasztva felsoroljuk a változók azonosítóit, majd ezt követi a típus megadása. A program második különbsége az utolsó értékadásban szereplő kifejezés, azaz az hogy itt az x változónak nem egy konkrét értéket adunk, hanem kiszámíttatjuk az i és j változók hányadosát. Idáig tehát tudunk változót deklarálni, annak értéket adni, illetve azt kiírni. de még nagyon hiányzik az, hogy egy változó értékét menet közben adjuk meg. Ezt a beolvasó utasítással érhetjük el. Bővítsük ki az előző programot úgy, hogy az i értékét a klaviatúráról olvassuk be: program negyedik; var i,j : integer; x : real; begin ReadLn(i); j:=6; x:=i/j; Write(x); end. Könnyen kitalálható, hogy a ReadLn(i) utasítás szolgál arra, hogy az i változóba a klaviatúráról értéket olvassunk be. Ezzel a programunk már teljessé vált, hiszen egyik rész sem hiányzik belőle. Megvan a programfej, a deklarációs rész, a programtörzs, valamint a programtörzsön belül van beolvasás, számítás és kiírás. Most tekintsük át az utóbbiakat külön-külön. 2.4.2. Olvasás billentyűzetről Itt két eljárást kell megemlíteni. Az egyik a Read a másik a ReadLn. Szintaktikájuk (a programbeli használatuknak illetve leírásmódjuknak szabálya) a következő: Read(változóazonosító); illetve ReadLn(változóazonosító); Mindkét eljárás a megadott azonosítójú változó értékét teszi egyenlővé a klaviatúráról beolvasott értékkel, a különbség mindössze annyi, hogy a ReadLn eljárás az Enter billentyűig kiolvassa a beírtakat, és az Enter-t figyelmen kívül hagyja. A különbség érzékeltetésére nézzünk meg két rövid programot: program beolvas1; var ch1,ch2 : char; begin Read(ch1); Read(ch2); Write(ch1,ch2); end. Ha ezt a programot lefuttatjuk, és a klaviatúrán egy a betűt és utána egy Enter-t ütünk, akkor a program a ch1 változóba beolvas egy karaktert, ez lesz az a betű, a ch2 változóba beolvas egy másik karaktert, ez lesz az Enter, majd ezeket kiírja és megáll. Ha viszont a két Read eljáráshívást ReadLn-ra cseréljük, akkor a program viselkedése megváltozik. Nézzük meg így a programot: program beolvas2; var ch1,ch2 : char; begin ReadLn(ch1); ReadLn(ch2); Write(ch1,ch2); end. Tegyük ugyanazt, mint az előbb, üssünk le egy a betűt és egy Enter-t. Ennek hatására a gép végrehajtja az első ReadLn utasítást, azaz a ch1 változóba beírja az a betűt, az Enter-t pedig - valamint ha valamit írtunk az a betű és az Enter közé - elfelejti. Most le kell ütnünk mondjuk a b billentyűt és az Enter-t, ennek hatására a ch2 értéke b lesz és a képernyőn megjelenik a program eredménye, az ab kiírás. A legnagyobb keveredést a Read működése okozhatja, ugyanis ha a program egy Read utasításhoz ér akkor megáll, és várakozik mindaddig, ameddig jelzést nem kap, hogy a kért értéket a klaviatúráról leolvashatja. Ez a jelzés pedig éppen az Enter leütése, azaz ameddig Enter-t nem ütünk, addig a változó értékével semmi sem történik. Igen ám, de ahogy az első példában is látszott, ekkor a változóba betöltődik az adott érték, és legalább egy Enter ottmarad, és összezavarhatja a következő beolvasást. A Read illetve a ReadLn eljárásokkal egyszerre több változót is beolvas-hatunk. Ekkor a szintaktikájuk a következő lesz: Read(változó1,változó2, ... ,változón) illetve ReadLn(változó1,változó2, ... változón) Karakter típusú változó esetén egyértelműen az n darab változóba az első n darab karakter kerül. Numerikus (real, integer) változók esetén az egyes értékeket egymástól el kell választani, ugyanis pl. ha három integer típusú változóba olvasunk be és a klaviatúrán az "1234" számsorozatot írjuk le, akkor nem lehet eldönteni, hogy az vajon 1 2 34, 1 23 4, vagy 12 3 4 akart lenni. Az értékek elválasztása a szóköz vagy az Enter billentyűvel történik. Numerikus érték beolvasásánál valamilyen számot mindenképpen be kell írnunk, ugyanis ha csak szóköz illetve Enter billentyűt ütünk le, akkor a gép tovább várakozik, egyéb, nem szám leütése esetén pedig hibát jelez és a programunk leáll. 2.4.3. Kiírás képernyőre Megint két eljárást kell ismertetnünk: a Write és a WriteLn. Ezen eljárások hívása a következő: Write(kiírandó); illetve Writeln(kiírandó); A két eljárás között mindössze annyi a különbség, hogy az első használata esetén a következő kiírás közvetlenül az előző kiírást követi, a második utasítás használata esetén a legközelebbi képernyőre íráskor pedig új sor kezdődik. Most nézzük meg, hogy mit és milyen formában tudunk kiírni, azaz mi szerepelhet a zárójelek között levő "kiírandó" helyén: először is, miként már láttuk szerepelhet " (aposztrófok) közé zárt szöveg, ebben az esetben a szöveg kerül kiírásra, aposztrófok nélkül. Például a WriteLn('Rendben'); utasítás hatására a képernyőn a Rendben szöveg jelenik meg. Láttuk azt is, hogy a "kiírandó" helyén szerepelhet változóazonosító is azaz, ha a szoveg egy string típusú változó akkor a szoveg:='Rendben'; WriteLn(szoveg); programrészlet hatása az előzővel megegyező. Természetesen nem csak szövegeket írhatunk ki, hanem számokat is. A WriteLn(56); utasítás eredménye a képernyőn 56 és ugyanígy ha i integer típusú, akkor a i:=56; WriteLn(i); hatására ugyancsak 56 jelenik meg a képernyőn. Ha ezek után, a sikereken felbuzdulva kipróbáljuk a WriteLn(3.14); utasítást, joggal várhatjuk, hogy az eredmény 3.14 lesz. Ehelyett azonban a képernyőn 3.1400000000E+00 jelenik meg. Ennek az az oka, hogy ha egy számban tizedespont is van, akkor azt a Pascal automatikusan real típusúnak veszi, és olyan formát keres amelyikben minden real típusú számot képes kiírni, ez a forma pedig a szám úgynevezett normál alakja. Ennek az első része tartalmazza a szám értékes jegyeit, a második pedig azt, hogy a tíz hányadik hatványával kell ezt a számot szorozni. Például a 123.456 ebben a formában 1.2345600000E+02. A számítógép szempontjából ez a módszer nagyon praktikus, az egyszerű felhasználó azonban nehezen tudja elviselni - így természetesen létezik jobb megoldás is. Ezt úgy hívják, hogy a kiírási formátum beállítása. Ismét egy példával szemléltetve, ha x real típusú változó akkor a következő programrészlet: x:=3.14; WriteLn(x:6:2); hatására x értéke összesen 6 karakteren lesz kiírva, és ezen belül 2 karaktert foglalnak le a tizedesjegyek, azaz a kiírt érték: 3.14 lesz. Magától érthetődik, hogy nem csak a real típusú számokat írhatjuk ki adott hosszúságon, hanem más típust is. A szoveg:='Rendben'; WriteLn(szoveg:12); programrészlet hatására a "Rendben" kiírás 12 karakteren jelenik meg, úgy hogy először 5 szóközt látunk, majd utána a szöveget. Itt természetesen a második kettőspont a hozzá tartozó számmal együtt fölösleges, sőt hiba, hiszen az a tizedesjegyek számát határozza meg, az pedig kizárólag real típusú számok esetén létezik. Ugyanúgy ahogy a Read illetve ReadLn eljárásnak több beolvasandó változója lehet, a Write illetve a WriteLn eljárás képes több értéket is kiírni, ekkor a kiírandó értékeket vesszővel választjuk el. Természetesen itt is figyelni kell, mert például az alábbi rövid program: program hibasiras; var a,b : integer; begin a:=11; b:=22; Writeln(a,b); end. eredménye a képernyőn 1122 lesz, és nem lehet látni, hogy hol a két szám között a határ. Ugyanez a program a Writeln(a:3,b:3); utasítás hatására 11 22 formájú eredményt szolgáltat. Nézzük meg most az előbbiekben leírt negyedik példaprogramot kicsit kibővítve Read illetve Write eljárásokkal. program irolvas; var i,j : integer; x : real; begin Write('Az osztando:'); ReadLn(i); j:=6; x:=i/j; WriteLn('Az oszto:6'); Writeln('Az eredmeny:',x:5:2); end. A programot elindítva a számítógép kiírja a képernyőre a következő üzenetet: Az osztando: és várakozik. Ezután be kell gépelnünk egy integer típusú számot és egy Enter-t. Ez a szám közvetlenül a kettőspont után jelenik meg, mivel Write eljárást használtunk. Az osztando:9 Tételezzük fel, hogy a 9 számot írtuk be. az Enter leütése után a gép végrehajtja a hátralevő utasításokat és kiírja: Az oszto:6; Az eredmeny: 1.50 Érdemes megfigyelni, hogy az idézeten belül ('Az oszto:6') szereplő :6 nem azt vezérli, hogy milyen hosszon kerül az idézet kiírásra, mert ehhez a kiírandó idézet után kellene állnia, hanem az idézet részeként lesz kiírva. 2.4.4. Számítás Értelemszerűen ez a program legösszetettebb része, hiszen programunk itt végez el minden olyan számítást, amiért létrehoztuk, a többi rész (deklaráció, beolvasás, stb ...) csak kiegészítés, bár feltétlenül szükséges. A számításoknál először az úgynevezett értékadó utasításokkal kell foglalkozni, amelyek segítségével változóknak lehet az értékét megadni. Értékadó utasításra példát már láttunk ebben a részben, hiszen gyakorlatilag képtelenség nélküle programot írni. Most nézzük meg az értékadó utasításokra vonatkozó legfontosabb szabályokat. Az értékadó utasítás formája mindig változóazonosító:=kifejezés; ahol a változóazonosító annak a változónak az azonosítója, amelynek értéket akarunk adni, a := jel azt jelenti, hogy a jel bal oldalán álló változó a jel jobb oldalán lévő kifejezés értékét kell hogy felvegye. Itt a következőkre kell nagyon figyelni. Az első, nem az értékadás formá-jával összefüggő szabály az, hogy minden változónak adjunk értéket. Ez nyilvánvalónak tűnik, mégis nagyon sokan elrontják, mert feltételezik, hogy a változó értéke a program futásának kezdetén pl. numerikus változó esetén nulla. Ez nagyon súlyos hiba, és adott esetben nehezen megtalálható hiba, mert a gép a deklarációs részben létrehozza a változót, és értékül azt adja, ami éppen azon a memóriacímen volt. A másik, már formai szabály az, hogy egy értékadó utasítással csak egy változónak adhatunk értéket, azaz például ha az a, b, c és d változónak is nullát akarunk értékül adni, akkor azt csak négy utasítással tudjuk megtenni: a:=0; b:=0; c:=0; d:=0; (megjegyzendő, hogy nem hiba, ha ezt a négy utasítást egy sorba írjuk). A harmadik fontos szabály az, hogy néhány esettől eltekintve az értékadó utasítás jobb és bal oldalán álló kifejezésnek azonos típusúnak kell lennie, azaz például az egyik leggyakoribb hibát az alábbi programban nézhetjük meg: Program hiba1; var i,j,k : integer; begin Write('i:'); ReadLn(i); Write('j:'); ReadLn(j); k:=i/j; WriteLn('k=',k:5); end. Ezt a programot nem tudjuk lefuttatni, ugyanis az osztás eredménye, azaz az értékadó utasítás jobb oldalán álló kifejezés értéke mindig real típusú, függetlenül attól, hogy a kifejezés értékének tört része nulla-e vagy sem. Ilyen hibával már eleve el sem indul a program, mivel nem lehet a programot lefordítani. A kritikus sort helyesen a k:=round(i/j); formában írhatjuk, ahol a round(i/j) az i/j kifejezést a kerekítés szabályai szerint kerekíti, és integer típusúvá alakítja. Az utolsó, ami ide kívánkozik, az értékadó utasítás működésével kapcsolatos. Az értékadó utasítás a változó értékét olyan módon határozza meg, hogy először kiszámolja a jobb oldalon álló kifejezés értékét, és a bal oldalon álló változó ezután veszi fel ezt az értéket. Ez nyilvánvalónak látszik, és azt hogy miért kell kiemelni, a következő utasítás mutatja meg: a:=a/2+b; Itt (és ez eléggé gyakori) az a változó szerepel az utasítás bal, valamint a jobb oldalán is, és beláthatatlan következményekkel járna, ha az a értéke a kifejezés értékének kiszámítása alatt megváltozhatna. Az eddigiek segítségével már írható komplett működőképes program, de ezek a programok még semmi másra nem jók, csak arra, hogy egy egyszerű számítást elvégezzenek. Legyen most a feladat a signum függvény meghatározása, azaz olvasson be a program egy real típusú számot, és ha a szám pozitív, akkor a sign(x)=1, ha a szám negatív, akkor a sign(x)=-1, ha pedig nulla akkor a sign(x)=0 üzenetet írja ki. A feladat megoldása nem okoz nagy gondot, hiszen ha x értéke nullától eltér akkor a sign(x) értéke az abs(x)/x kifejezéssel kiszámítható. A problémát az jelenti, hogy ha x értéke nulla akkor ezt a műveletet a számítógép nem tudja elvégezni, és hibaüzenettel leáll. Ahhoz hogy ezt elkerüljük, rendelkezésünkre áll az úgynevezett feltételes elágazás. Ekkor egy feltételtől függően hajt végre a gép egy elágaztatást, vagy pedig a következő két utasítás valamelyikét. Ennek az utasításnak a formája: if feltétel then utasítás; Ebben az esetben a then kulcsszó után álló utasítás csak akkor hajtódik végre, ha az if és then között álló feltétel teljesül. A másik esetben if feltétel then utasítás1 else utasítás2; Itt az if és then között álló feltétel teljesülése esetén csak az utasítás1 hajtódik végre, az utasítás2 nem, ha pedig az említett feltétel nem teljesül, akkor csak az utasítás2 hajtódik végre, az utasítás1 nem. Nézzük meg ezeket az előbb említett signum függvény kapcsán: Program signum1; var a,sign : real; begin Write('A szám:'); ReadLn(a); sign:=0; if a<>0 then sign:=abs(a)/a; WriteLn('sign(x)=',sign:3:0); end. illetve Program signum2; var a : real; begin Write('A szám:'); ReadLn(a); if a=0 then WriteLn('sign(x)= 0.') else WriteLn('sign(x)=',abs(a)/a:3:0); end. Az első programban a sign(x) értékét nullának feltételeztük, és ha a beolvasott szám nullától eltért, akkor változtattuk meg az értékét. A második esetben nem feltételeztünk semmit, hanem a beolvasott szám értékétől függően írtuk ki sign(x) értékét. Fontos formai szabály, hogy a pontosvessző az egész if utasítást zárja le, tehát második forma használata esetén az else kulcsszó előtt semmiképpen sem szerepelhet pontosvessző. A feltételes elágazásoknak a Pascal ismeri egy másik formáját is. Az if utasítás ugyanis legfeljebb kétirányú elágazást enged meg, hiszen egy feltétel mindig egy logikai kifejezés (boolean típus) amely vagy igaz, vagy hamis. Kézenfekvő, hogy legyen egy olyan elágazás is, amely más típus esetén is lehetővé teszi, hogy egy kifejezés értékétől függően hajtódjon végre a következő utasítás. Ennek formája case kifejezés of érték1 : utasítás1; érték2 : utasítás2; ... értékn : utasításn; else utasitás end; Az utasítás használatát ismét egy példán mutatjuk be. Irjunk programot, amely egy pont koordinátáit olvassa be, és kiírja, hogy a koordinátarendszer melyik negyedébe esik a pont, illetve, ha a pont éppen valamelyik tengelyre esik, azt is jelzi. Az x és az y változók tartalmazzák a pont kordinátáit. Ha a pont egyik tengelyre sem esik, akkor (sign(x)+1)*10+(sign(y)+1) értéke rendre a 22, 20, 0 illetve a 2 értéket veheti fel. Nézzük most a programot: program siknegyed; var x,y : real; signx,signy : integer; begin Write('x='); ReadLn(x); Write('y='); ReadLn(y); if x=0 then signx:=0 else signx:=round(abs(x)/x); if y=0 then signy:=0 else signy:=round(abs(y)/y); case (signx+1)*10+(signy+1) of 22 : WriteLn('Első síknegyed'); 20 : WriteLn('Második síknegyed'); 0 : WriteLn('Harmadik síknegyed'); 2 : WriteLn('Negyedik síknegyed'); else WriteLn('A pont éppen tengelyre esik') end; end. A case utasítás használatánál a következőkre kell figyelni. Először, hogy a case kulcsszót követő kifejezés (ezt hívják az utasítás szelektorának) csak úgynevezett sorszámozott típusú lehet. Nem lehet például az eddig tárgyalt típusok közül real vagy string. A másik fontos szabály a kettőspontok és pontosvesszők használatára vonatkoznak. Az utasítás törzsében fel kell sorolni a szelektor azon az értékeit, amelyek szerint végre akarjuk hajtani az utasítást. A jelen példában, ha a szelektor értéke 22, 20, 0 illetve 2 akkor meghatározott szöveget kell kiírni. Bármely más értékre az else után álló szöveg kerül a képernyőre. A szelektor lehetséges értékeit rendre egy-egy kettőspont követi. A kettőspont után található az az utasítás, amelyet az adott érték esetén végre kell hajtani, majd minden egyes utasítást pontosvessző zár. Az utolsó választási lehetőség az else ág. Ez csak akkor hajtódik végre, ha a szelektor egyik felsorolt értékkel sem egyezett meg. A lehetséges szelektorértékeknél alkalmazott szabállyal ellentétben az else szót nem követheti kettőspont, és az utána álló utasítást nem kell pontosvesszővel zárni. Az if utasítás szabályaival ellentétben a case utasításnál nem hiba, ha közvetlenül az else kulcsszó előtt pontosvessző áll. Ha a felsorolt szelektorértékektől eltérő érték esetén nem akarunk semmit sem csinálni, akkor lehetőségünk van az else részt kihagyni. A case utasítást mindig end kulcsszóval zárjuk, ez jelzi, hogy a program ne keressen több szelektorértéket. Az eddigi ismeretek birtokában elvileg már majdnem minden feladatot meg lehet oldani. A programozás alapvető részeiről szóló fejezethez még egy kiegészítés kívánkozik. Mint már jó néhányszor, most is nézzünk meg egy példát. Olvassunk be tíz számot, számítsuk ki az átlagát, és határozzuk meg az átlagtól való legnagyobb eltérést. A program algoritmusa a következő: Először beolvassuk a tíz értéket, és ugyanakkor össze is adjuk egymással. Ezután ezt az összeget elosztjuk tízzel, így megkapjuk az átlagot. Végül ismét végignézzük a számokat, és megvizsgáljuk az átlagtól való eltérést. Most gondoljuk végig, hogy milyen változókat használjunk. Előszöris be kell olvasni a tíz számot. Ez az eddigiek alapján tíz real típusú változót jelentene, de ezt nagyon körülményes lenne kezelni, ezért egy, az eddigiekben nem szereplő változótípust fogunk használni, a tömböt. Ennek lényege, hogy például a példánkban szereplő tíz változót azonos azonosítóval, valamint egy sorszámmal jelöljük mégpedig olyan módon, hogy az első beolvasott szám a szam[1], a második a szam[2] és így tovább. Természetesen, miután a tömb elemek csoportjából áll, a tömböt alkotó elemeknek van saját típusa, azaz tömböt képezhetünk real, integer illetve tetszőleges egyéb típusú elemekből is. A tömbváltozók deklarálása a deklarációs részben történik, mégpedig - a jelen példára hivatkozva - a következő formában: var szam : array [1..10] of real; ahol a szam a tömb azonosítója, az array kulcsszó jelzi, hogy tömbváltozóról van szó, a szögletes zárójelek közé [ ] írt két szám pedig a lehetséges legkisebb és legnagyobb indexet jelöli ki. A megadás módjából kitalálhatjuk, hogy az index tetszőleges számtól tetszőleges számig terjedhet, így például nem hibás a var tomb : array [-9..100] of integer; deklaráció sem. Ezután a kitérő után térjünk vissza a programunk változólistájának a megtervezéséhez. Az eddigiek alapján lesz egy tízelemű real számokból álló tömbünk. Ezenkívül kell egy változó, amelyik az átlagot fogja tartalmazni, ennek neve atl lesz, típusa pedig real; A következő változónk pedig az eltérés értékét mutatja meg, ennek neve legyen delta, típusa pedig szintén real. A program írása során még egy változót fogunk használni, amelyik azt mutatja meg, hogy a tömbnek éppen hányadik elemével foglalkozunk (ezt segédváltozónak hívjuk, mert a számításokban közvetlenül nem vesz részt). A segédváltozónak az i azonosítót adjuk, és integer típusúnak deklaráljuk. Mielőtt azonban a program írásához fognánk, térjünk vissza az algoritmushoz. A program első részében be kell olvasnunk 10 számot. Ezt az eddigi ismereteink szerint beolvashatjuk egyenként a következő programrészlet segítségével: Write('-'); ReadLn(szam[1]); Write('-'); ReadLn(szam[2]); ... Ezt a megoldást azonban rögtön el is kell felejteni, annyira rossz. A tíz szám beolvasása ugyanis ugyanannak az utasításnak a tíz esetben történő megismétlését jelenti. Ilyen ismétlés a programok írása során rendkívül sokszor előfordul, ezért az ilyen ciklikusan ismétlődő műveletekhez a programnyelvek segítséget nyújtanak. Ezeket az utasításokat hívjuk átfogó néven ciklusutasításoknak. A Pascal programnyelv háromféle ciklusutasítással rendelkezik, most nézzük azt, amelyikre a programunkhoz szükségünk van. Ennek formája a következő: for ciklusváltozó:=kifejezés1 to kifejezés2 do utasítás; illetve for ciklusváltozó:=kifejezés1 downto kifejezés2 do utasítás; Ezeknek az utasításoknak a hatására a for-t követő ciklusváltozó értéke először a kifejezés1 értékét veszi fel, ezután végrehajtódik az utasítás, majd az említett változó értéke to esetén eggyel nő, downto esetén eggyel csökken, és újra végrehajtódik az utasítás, mindaddig, míg a változó értéke a kifejezés2 értékét el nem éri. Nézzük ezután a program listáját: program atlag1; var atl,delta : real; szam : array [1..10] of real; i : integer; begin atl:=0; for i:=1 to 10 do begin Write(i,'. szám: '); ReadLn(szam[i]); atl:=atl+szam[i]; end; atl:=atl/10; delta:=abs(atl-szam[1]); for i:=2 to 10 do if delta < abs(atl-szam[i]) then delta:=abs(atl-szam[i]); WriteLn('Az átlag=',atl:8:3); WriteLn('A legnagyobb eltérés=',delta:8:3); end. Ebben a programban használtuk a fejezet elején említett, de részletesen nem tárgyalt begin-end utasítászárójeleket, ezeknek a szerepe a következő: A Pascal a feltételes és a ciklusutasításokban (egy rövidesen szóba kerülő kivételtől eltekintve) egy utasítás feltételtől függő illetve ciklikus végrehajtását teszi lehetővé. Ez azonban az esetek többségében nem elegendő, így, például a példánkban is ugyanabban a ciklusban ki kellett írni a képernyőre valami információt, amely jelzi, hogy a gép adatot vár, be kellett olvasni az adatot, és az atl nevű változó értékét meg kellett változtatni. Erre szolgált az utasítászárójel, amelynek hatására a Pascal a begin és end között álló utasításokat egy összetett utasításként kezeli. A teljesség kedvéért meg kell még említeni a Pascalban használt másik két ciklust is. Ezek, az említett for ciklustól eltérően nem adott számú esetben ismétlik meg a ciklusmagban szereplő utasítást, hanem egy feltételtől függően. Az első ilyen ciklusutasítás a while feltétel do utasítás; Itt a program az utasításhoz érve először megvizsgálja a feltételt, amely mindig egy logikai kifejezés. Ha ennek értéke true, azaz a kifejezés teljesül, akkor végrehajtja az utasítást és újra vizsgálja a feltételt, és ezt ismétli mindaddig amíg a feltételként szereplő kifejezés értéke false nem lesz. A másik itt említett ciklusutasítás a repeat utasítás1; utasítás2; . . . utasításn; until feltétel; alakú. Ez az utasítás az előbb említett kivétel, itt a ciklusmagja nem csak egy - egyszerű vagy összetett - utasítást tartalmazhat, hanem tetszőleges számút. A ciklusban a repeat és az until kulcsszavak közötti utasítások fognak ismétlődni. Ennek a ciklusnak a működése is eltér a while ciklustól, ugyanis itt először végrehajtódnak a ciklusban szereplő utasítások, utána a gép megvizsgálja az until után szereplő kifejezés értékét, és ha ez false (hamis) akkor ismétli meg a ciklus utasításait, ha pedig true (igaz), akkor elhagyja a ciklust, és a program a ciklus utáni utasítással folytatódik. Az eddig leírtak alapján már nagyon sok program algoritmusa, szerkezete felépíthető. A könyvben megtalálható a Pascal nyelv összes utasítása, ezeknek szabályai, működése, és sok példa, remélhetőleg ezeknek alapján mindenki eljut odáig, hogy önállóan meg tudjon oldani minden programozási feladatot, és még örömét is lelje benne.