2014. szeptember 9., kedd

TP


Az algoritmus fogalma, tervezése

Mit is értünk algoritmuson? Ha egy problémát szeretnénk megoldani, egy feladatot szeretnénk elvégezni, meg kell találnunk a megoldáshoz vezető utat. Ez az út lépésről lépésre végrehajtandó instrukciók sorozatából áll. Ha az utasítások, instrukciók sorozatát, azaz az algoritmust kitaláltuk, megterveztük, akkor túl vagyunk a feladat megoldásának legnehezebb részén. Bizonyos algoritmusokat végrehajthatunk mi magunk is vagy megbízhatunk vele valaki mást. A mindennapi életben is gyakran találkozhatunk algoritmusokkal (pl. egy recept, egy bútor összeszerelési útmutatója stb.) Kurzusunk során a számítógépet utasítjuk algoritmusaink végrehajtására.
Nézzük az alábbi egyszerű algoritmust!

Vágj egy szelet kenyeret!
Vajazd meg!
Sózd meg!

Itt az utasításokat egyszerűen egymás után kell végrehajtanunk. Ezt nevezzük szekvenciának. Előfordulhat, hogy egy feltételtől függ az, hogy egy utasítást végre kell-e hajtanunk, vagy hogy melyik utasítást kell végrehajtanunk. Ekkor beszélünk szelekcióról (nevezik még elágazásnak is). A vajas kenyér készítés algoritmusát kiegészítve láthatunk példát erre.

Vedd elő a kést!
Ha koszos, akkor
Mosd el!
Vágj egy szelet kenyeret!
Vajazd meg!
Sózd meg!

Elképzelhető, hogy egy tevékenységet többször kell ismételnünk. Ezt iterációnak, vagy ciklusnak nevezzük.

Vedd elő a kést!
Ha koszos, akkor
Mosd el!
Ismételd ötször:
Vágj egy szelet kenyeret!
Vajazd meg!
Sózd meg!

Az utolsó három utasítást nevezzük ciklusmagnak. Nem mindig ismerjük előre az ismétlések számát, hanem egy ciklusfeltételtől függ az, hogy a ciklusmagot újra végre kell-e hajtanunk. Ezt a feltétel vizsgálhatjuk a ciklusmag minden egyes végrehajtása előtt, vagy után is. Ezen ciklusok szokásos elnevezései: elöltesztelő feltételes (kezdőfeltételes) ciklus, illetve hátultesztelő feltételes (végfeltételes) ciklus. Az alábbi példa egy végfeltételes ciklust tartalmaz:

Vedd elő a kést!
Ha koszos, akkor
Mosd el!
Ciklus kezdődik:
Vágj egy szelet kenyeret!
Vajazd meg!
Tegyél rá szalámit!
Ismételd, ha van még szalámi!
Ciklus vége

Figyelje meg, hogy a ciklus elejét és végét egyértelműen jeleznünk kell! Az elágazásokkal, ciklusokkal a II., illetve a III. tanulási egységben fogunk részletesebben foglalkozni. Láttuk a három fő algoritmuselemet (hívják még vezérlőszerkezetnek is): szekvencia, szelekció, iteráció. A csak ezen három vezérlőszerkezetet használó programokat strukturált programoknak nevezzük.
Mi az algoritmusainkat számítógéppel szeretnénk végrehajtatni. Ehhez az algoritmust egy a gép által érthető nyelven, azaz egy programozási nyelven kell megfogalmaznunk. Így el is jutottunk a program fogalmához. Nézzük, hogyan definiálja az Informatika SH atlasz [2]! „A program egyértelmű szabályok segítségével felírt algoritmus, amely ebben az alakban egy meghatározott számítógépbe táplálható és azon lefuttatható, végrehajtható.”
A számítógépes algoritmusokkal szemben szigorúbbak a követelmények, mint a hétköznapi életben használt algoritmusok esetén. Nézzük, melyek a számítógépes algoritmusok főbb jellemzői:
- Az algoritmus lépések sorozata, amelyben minden lépésnek egyértelműen végrehajthatónak kell lennie.
- Véges számú lépésből áll, véges idő alatt befejeződik.
- Az algoritmus általában a bemenő adatokat feldolgozva kimenő adatot (adatokat) állít elő.
- Determinisztikus, azaz ugyanazon bemenő adatokra tetszőleges számú végrehajtás esetén ugyanazt az eredményt szolgáltatja, azaz egyértelmű a lépések sorrendje.
- Legyen hatékony, azaz úgy írjuk le az algoritmus, hogy minél gyorsabban végre lehessen hajtani!
- Legyen megbízható!
- Oldja meg a feladatot!

A legtöbb program tehát a bemenő (input) adatokat feldolgozva kimenő (output) adatokat szolgáltat. A program készítésekor az algoritmus megalkotása előtt meg kell terveznünk az adatok belső struktúráját, azaz hogy az algoritmus milyen adatszerkezeteken dolgozzon. Egy nagy programozási feladatot részekre, modulokra kell bontani, majd az egyes megoldott modulokat össze kell illeszteni. Kérem, tanulmányozza a moduláris programozásról szóló fejezetet az [1] irodalomban (2.2 Algoritmus tervezése, moduláris programozás; 26-27. old.)!

1.2 Algoritmus leíró eszközök

Egyszerűbb programozási feladatok megoldásakor az algoritmust „fejben” is megtervezhetjük, kitalálhatjuk. Hosszabb, összetettebb programok írásakor a kódolást, azaz az algoritmus egy adott programozási nyelven történő megvalósítását megelőzi az adatok és az algoritmus tervezése. Az algoritmusok tervezésére, leírására többféle módszer létezik. Tekintsünk át ezek közül kettőt, a folyamatábrát és a mondatszerű leírást, melyeket a későbbi programozási példák, feladatok kapcsán is használni fogunk.
A folyamatábra az algoritmus lépéseinek egymásutániságát, folyamatát szemlélteti többé-kevésbé szabványos jelölésrendszerrel. A mondatszerű leírás, más néven pszeudokód már közel áll egy programozási nyelven elkészített kódhoz. Magyar nyelven bizonyos szabályokat és formákat betartva írjuk le az algoritmust. Nézzük az egyes algoritmus elemek megvalósítását mindkét jelölésrendszerrel!

Be- és kivitel:

Be: változó
Ki: kifejezés





A későbbiek során (3.3 fejezet) még áttekintjük a változó és a kifejezés fogalmát.

Szekvencia:

Tevékenység1
Tevékenység2
Tevékenység3


A tevékenység lehet például egy értékadó utasítás (ld. 3.3 fejezet).

Szelekciók:
Egyágú

Ha Feltétel akkor
Tevékenység(ek)
Elágazás vége


Figyelje meg, hogy a folyamatábrával milyen szemléletesen tudjuk ábrázolni az egyes algoritmuselemek működését! Pl. a fenti ábrán látható egyágú szelekció: ha a Feltétel igaz, akkor végrehajtódik a Tevékenység, ha hamis, akkor a Tevékenység utáni utasítással folytatódik tovább az algoritmus.

Kétágú


Ha Feltétel akkor
Tevékenység(ek)1
Egyébként
Tevékenység(ek)2
Elágazás vége

A későbbiek során többágú szelekciókkal is fog még találkozni.

Ciklusok:
Elöltesztelő – a ciklusmag addig ismétlődik, amíg a feltétel igaz

Ciklus amíg Feltétel
Tevékenység(ek)
Ciklus vége



Hátultesztelő: a ciklusmag addig ismétlődik, amíg a feltétel igazzá nem válik

Ciklus
Tevékenység(ek)
Mígnem Feltétel
Ciklus vége










Növekményes (számláló) ciklus - itt előre ismerjük az ismétlések számát:

Ciklusváltozó:=...tól...ig
Tevékenység(ek)
Ciklus vége


Ez utóbbi ciklusfajtát csak a teljesség kedvéért adtuk itt meg, bővebben a III. tanegységben fog róla tanulni.

Kérem, tanulmányozza az [1] irodalomban is a folyamatábra jelöléseit (32-34 old.)! Figyelje meg, hogy ott az adatbevitelre és adatkiírásra más jelölést használnak!

Nézzük a szendvicskészítés végfeltételes ciklust tartalmazó verziójának folyamatábráját! Figyelje meg, hogy itt a ciklus a feltétel hamis értéke esetén fejeződik be, míg a fenti felsorolásban az igaz érték esetén, amely megfelel az általunk használt Pascal nyelv szabályainak (ld. III. tanegység).

A folyamatábra mellett elterjedt algoritmus leíró módszerek még a struktogram és a Jackson-ábra, amelyekről az [1] irodalomban kiegészítő tananyagként olvashat.
Ha egy algoritmust számítógépen akarunk lefuttatni, akkor azt egy programozási nyelv segítségével kell leírnunk. (Lapozzon vissza néhány oldallal korábbra a program fogalmához!)

Összefoglalva az eddigieket:
Egy program adatokat dolgoz fel. Meg kell terveznünk ezen adatok belső struktúráját és a programozási feladat megoldásához vezető lépések sorozatát, azaz az algoritmust. A strukturált programok felépíthetők három vezérlőszerkezetből: szekvenciából, szelekcióból és iterációból.

Kérem, oldja meg a gyakorló és az ellenőrző feladatokat!

1. Melyik az a három vezérlőszerkezet, amelyekből az algoritmusokat felépíthetjük?

2. Melyik a kakukktojás, és miért?
A. folyamatábra      B. ciklus      C. mondatszerű leírás
D. program

3. Párosítsa az összetartozó fogalmakat!
1. program A. véges lépés
2. szelekció B. számítógépen futtatható
3. programozási feladat C. vezérlési szerkezet
4. nagy programozási feladat D. moduláris programozás
5. algoritmus E. adatok tervezése,
   algoritmus tervezése

4. Milyen algoritmuselemekre szolgálnak például az alábbi mondatok?
A. Ha esik az eső, akkor moziba megyek, egyébként pedig a kertben dolgozom.
B. Annyi vödör vizet hozok, amíg tele nem lesz a dézsa.
C. Elmosogatok öt poharat.
D. A kertben dolgozom, majd moziba megyek.
E. Ha lesz kedvem, kiporszívózok.

5. A szendvicskészítés példájában feltételeztük, hogy legalább egy szelet szalámi van, ezért végfeltételes ciklust alkalmaztunk. Alakítsa át úgy az algoritmust, hogy akkor is jól működjön, ha nincs szalámi (természetesen ekkor egy szendvicset sem készítünk.) Írja le az algoritmust folyamatábrával és mondatszerű leírással is.


1. Melyik algoritmuselemben szerepel feltétel?
A. szekvencia    B. egyágú szelekció    C. kétágú szelekció
D. ismert lépésszámú (számláló) ciklus    E. feltételes ciklus

2. Melyik algoritmusrészlet a hatékonyabb?
A.
Ha a szám nagyobb 10-nél akkor
A szám új értéke legyen 10
Egyébként
Ha a szám kisebb -20-nál akkor
A szám új értéke legyen –20
Elágazás vége
Elágazás vége

B.
Ha a szám nagyobb 10-nél akkor
A szám új értéke legyen 10
Elágazás vége
Ha a szám kisebb -20-nál akkor
A szám új értéke legyen –20
Elágazás vége

3. Készítse el egy olyan, a hétköznapi életből vett algoritmus folyamatábráját és mondatszerű leírását, amelyben szerepel mindhárom vezérlőszerkezet! (A folyamatábra szöveg-szerkesztővel való elkészítéséhez használhatja az MS Word Rajz/Alakzatok/Folyamatábra elemeit.)



2. Programozási nyelvek

Ön az előző fejezetben megtanulta, hogy egy programozási feladat megoldása az adatok és az algoritmus tervezésével indul. Ha algoritmusunkat egy számítógépen szeretnénk végrehajtani, akkor a gép által érthető nyelven kell leírnunk azt, azaz egy programot kell írnunk. A számítógépek központi feldolgozó egysége (CPU, amelyet a mikroprocesszor valósít meg) rendelkezik egy utasításkészlettel, azaz bizonyos kódokat beolvasva a memóriából, előre meghatározott módon működik. Ezek az ún. gépi kódok olyan alapvető műveleteket valósítanak meg, mint például: adatmozgatás, matematikai alapműveletek, összehasonlítás. Ezen kódokból felépülő, a processzor által közvetlenül végrehajtható programot nevezzük gépi kódú programnak. Ilyen például a PC-k világában az exe és com kiterjesztésű program. A gépi kódok nehezen megjegyezhető bitsorozatok, azaz kettes, vagy tizenhatos számrendszerben felírt számok. Közvetlenül gépi kódú programot nem szoktunk írni. Valamivel ember közelibb az Assembly nyelv, amelyek a kódokat egy kis szócskával, betűcsoporttal, az ún. emlékeztetővel (mnemonikkal) helyettesítik. Például az összeadás mnemonikja az ADD szócska. A gépi kódot és az assemblyt alacsony szintű programozási nyelvnek nevezzük, hiszen ezek a géphez állnak közel. Alacsony szintű nyelven nehéz programozni, hiszen nagyon apró lépésekből kell felépítenünk az algoritmust. A másik hátrány az, hogy a különböző típusú számítógépeknek az utasításkészlete, és így a gépi, illetve Assembly nyelve is különböző. Bátorításként közöljük, hogy Ön ezen kurzus során nem fog kapcsolatba kerülni az Assembly programozással.
A hátrányok megszüntetésére létrehozták a magas szintű programozási nyelveket. Ezek az emberi nyelvhez (többnyire az angol nyelvhez) állnak közel, és egymáshoz is többé kevésbé hasonlítanak. Ön az előző fejezetben megismert vezérlőszerkezeteket (szekvencia, szelekció, iteráció) könnyedén le tudja majd írni egy magas szintű programozási nyelv, konkrétan a Turbo Pascal eszközeivel.
A számítógép processzora csak a gépi kódot tudja végrehajtani, a magas szintű nyelveken és az Assemblyben megírt programot nem. Ezeket tehát ahhoz, hogy futtathatóvá váljanak, át kell alakítanunk, le kell fordítanunk gépi kódúvá. Az átalakítást fordítóprogram (compiler) végzi. A fordítóprogram az emberhez közel álló forrásprogramot (source) alakítja tárgykóddá (object code), amely már gépi kódú utasításokat tartalmaz. A lefordított program már vagy futtatható, vagy más programokkal összeszerkesztve válik futtathatóvá. Ezt egy újabb program, a programszerkesztő (linker) végzi. Ön a programozói munkája során keretrendszert, ún. fejlesztői környezetet fog használni (ld. II. tanegység), melyben a fordítás és a szerkesztés legtöbbször automatikusan történik.
A forrás kódú programnak meg kell felelnie az adott programozási nyelv szabályainak, azaz szintaktikájának. Ezen szabályok megsértését szintaktikai hibának nevezzük. Ez lehet akár egy véletlen elgépelés eredménye is. A fordítóprogram másik fontos feladata a tárgykód előállítása mellett az, hogy a forrásprogram nyelvtani, azaz szintaktikai ellenőrzését elvégezze. A compiler csak szintaktikailag hibátlan forrásprogramból képes tárgykódot generálni. Sajnos, ekkor még nem nyugodhatunk meg, hiszen a program futása közben is felléphet hiba (futási hiba), például nullával való osztás. Sőt előfordulhat, hogy egy látszólag hibátlan programot készítettünk, amely mégsem pontosan azt csinálja, amit szeretnénk, amit a program specifikációja előír. Ekkor logikai hibát, más néven szemantikai hibát tartalmaz a programunk. A futó program tesztelésének a célja ezen hibák felderítése.

Ön tehát egy magas szintű programozási nyelven, a Turbo Pascal nyelven fogja algoritmusait kódolni. A forráskód megírását, a fordítást, szerkesztést, futtatást, a program tesztelését mind el tudja majd végezni egy keretrendszer, az integrált fejlesztői környezet segítségével (IDE, Integrated Developing Environment). A keretrendszert a turbo.exe, vagy a tpx.exe programok valósítják meg. A fejlesztői környezetben menüből és gyorsbillentyűkkel vezérelhetően elvégezheti a legfontosabb szövegszerkesztési műveleteket (mentés, megnyitás, másolás, stb.), lefordíthatja (Compile menü), futtathatja programját (Run menü). A futtatás előtt a rendszer automatikusan elvégzi a fordítást és a szerkesztést. A keretrendszer részletes leírását megtalálhatja az irodalmakban ([1] 67-80 old.; [3] 23-63 old.), illetve mi is foglalkozunk még vele a III. tanegységben.


6. Mi a különbség az alacsony szintű és a magas szintű programozási nyelvek között?

7. Rakja helyes sorrendbe:
A. fordítás, B. futtatás, C. forráskód írása, D. programszerkesztés

8. Mi tartozik a fordítóprogram feladatai közé?
A. forráskódból tárgykód előállítása
B. szemantikai hibák felismerése
C. tárgykódból futtatható kód előállítása
D. szintaktikai hibák felismerése


3. Programozási alapfogalmak

Ön az alapvető programozási ismereteket a Turbo Pascal nyelv segítségével fogja elsajátítani. A Pascal programozási nyelv alapjait Niklaus Wirth definiálta a 70-es évek elején. A Pascal magas szintű, általános célú, strukturált programnyelv, egyszerű eszközrendszerrel, szigorú szintaktikai és szemantikai szabályokkal. A Standard Pascal kibővített mikrogépes (elsősorban IBM PC) változata a Borland cég által kifejlesztett Turbo Pascal. A nyelv elsajátítása jó kiindulópont lehet a haladóbb programfejlesztése rendszerek felé (Object Pascal, Delphi).
Nézzünk egy példaprogramot, amelyen keresztül bemutatjuk Önnek a Turbo Pascal programok szerkezetét, építőelemeit!

Példa
8 fős bankettet szervezünk. Programot szeretnénk írni, amely a vacsora egyes fogásainak (leves, főfogás, savanyúság) árából kiszámítja az egy főre eső költséget és az összköltséget.

program Bankett;
var
   Leves, Fofogas, Savanyu, EgyFo, Ossz: integer;

begin
  {A fogások árainak beolvasása}
  Write('A leves ára: ');
  ReadLn(Leves);
  Write('A főfogás ára: ');
  ReadLn(Fofogas);
  Write('A savanyúság ára: ');
  ReadLn(Savanyu);
  {Az egy főre eső költség kiszámítása és
    kiírása}
  EgyFo := Leves + Fofogas + Savanyu;
  WriteLn('Az egy főre eső költség: ', EgyFo,
           'Ft');
  {Az összköltség kiszámítása és kiírása}
  Ossz := 8 * EgyFo;
  WriteLn('Az összköltség: ', Ossz, 'Ft');
  ReadLn;
End.



3.1 A Turbo Pascal program építőelemei

A program olyan szöveg, amelynek eleget kell tennie a programozási nyelv szabályainak (szintaktikájának). Vegyük sorra, hogy a Turbo Pascal programok milyen építőelemekből állhatnak (a példák a Bankett programunkban szerepelnek):

- Szimbólumok, például: := : ; + .

- Elválasztójelek, például: szóköz, sorvégjel, megjegyzés.
A megjegyzést a fordítóprogram nem veszi figyelembe, hanem a forrásprogram olvasójának szól. Célszerű programunkat megjegyzésekkel, magyarázatokkal ellátni, hogy azt a későbbiek során könnyebben tudjuk értelmezni, esetleg módosítani. A megjegyzést általában kapcsos zárójelek közé tesszük, de használható a (* ...*) szimbólumpár is.

- Fenntartott szavak, például: program, var, begin, end.
Ezeknek a nyelv tulajdonít értelmet, másra nem használhatjuk őket. Más néven kulcsszavak.

- Azonosítók
Nevek, melyekkel a program bizonyos elemeit azonosíthatjuk (például az adatok tárolására szolgáló változóinkat). Lehetnek előre definiált azonosítók (standard azonosítók, pl. integer, Write), melyeket felhasználhatunk, hiszen a nyelv ismeri őket, valamint általunk választott nevek, mellyel saját programelemeinket azonosítjuk (pl. Leves, Savanyu). Az azonosítók az angol ABC betűiből, számjegyekből és aláhúzás jelekből állhatnak, de nem kezdődhetnek számjeggyel. Lényeges, hogy egyedinek kell lenniük, nem lehet két egyforma azonosító a programban. Vigyázzunk, ékezetes karakterek nem szerepelhetnek benne.

- Számkonstansok, például: 8
Az adattípusok tárgyalása során foglalkozunk majd bővebben a számkonstansokkal (II. tanegység).

- Szövegkonstans, például: 'A leves ára: '
Karakterlánc, vagy string konstansnak is nevezzük. Aposztrófok közötti tetszőleges ASCII karakterekből álló szöveg.

A Turbo Pascal, ellentétben néhány más nyelvvel, a szövegkonstans kivételével nem különbözteti meg a kis- és nagybetűket (pl. a Leves és a leves azonosítók ugyanarra vonatkoznak).


3.2 A Turbo Pascal program szerkezete

A Pascal programok három részből állnak:
- Programfej: a program kulcsszóval kezdődik, majd egy azonosító követi (példánkban Bankett), pontosvesszővel zárjuk le. Nem kötelező, de célszerű a használata.
- Deklarációs rész: itt kell felsorolnunk a program következő, végrehajtó részében használt programelemeinket, melyek fajtáit Ön majd sorra meg fogja tanulni. A példában az adatok tárolására szolgáló változókat deklaráltuk, a var kulcsszót követően. A változók azonosítóinak vesszővel elválasztott felsorolása mellett kettőspont után megadtuk azok típusát is (integer). (A 3.3 fejezetben foglalkozunk részletesebben a változókkal.)
- Végrehajtó rész: utasítások sorozatából áll, amelyek a programozási feladat algoritmusát valósítják meg. A begin kulcsszóval kezdődik, és az end kulcsszóval, valamint egy ponttal zárul. Láthatja a példaprogramban, hogy minden utasítást pontosvesszővel zártunk le. Ez egy kis félreértésre adhat okot, hiszen a pontosvessző nem az utasítások lezárására, hanem egymástól való elválasztására szolgál. Az utolsó utasítás (ReadLn) után el is hagyhattuk volna. Sőt vannak olyan helyzetek, amikor nem szabad az utasítást pontosvesszővel lezárni.

3.3 Értékadó utasítás

A leggyakrabban használt utasítással, ahogy a neve is mutatja, egy változónak adhatunk új értéket. Szintaktikája (nyelvtana) :
változó := kifejezés

Példa:
x := 2 * ( x + y ) + sin(alfa)

Az értékadás jele a :=, a bal oldalán mindig egy változónak kell állnia, a jobb oldalán pedig egy kifejezésnek. Mit jelentenek a programozásban ezek a fogalmak?

Változó
Már találkoztunk a változó fogalmával, nézzük pontosabban, melyek a főbb jellemzői. A programok a bemenő adatokat feldolgozva kimenő adatokat produkálnak. A változókban tárolhatjuk ezen adatokat, és ahogy az elnevezés is mutatja, az értékük a feldolgozás során változhat. A Pascal nyelvben minden változót deklarálnunk kell, azaz meg kell adnunk a nevét (ez egy azonosító), valamint a típusát. A típus meghatározza, hogy milyen jellegű adatokat tárolhatunk az adott változóban. A változóhoz tartozik egy memóriacím, hiszen a változók értékét a memóriában tárolja a rendszer. Összefoglalva, a változónak négy komponense van: neve (azonosítója), értéke, címe, típusa.

Kifejezés
A kifejezések formálisan operandusokból, műveleti jelekből (operátorok) és kerek zárójelekből épülnek fel. Az operandusok konstansok (pl. 2), változók (pl. x, alfa) és függvényhívások (pl. sin(alfa)) lehetnek. (A függvényekkel a II. tanegységben foglalkozunk.) A kifejezések egy értéket képviselnek, amely meghatározását nevezzük kiértékelésnek. A műveletek kiértékelésének sorrendjét precedenciájuk (prioritásuk, elsőbbségük) szabja meg. A Pascal nyelv  precedencia szintjei az alábbiak:
1. NOT, +, -, @ (egy operandusú műveletek)
2. *, /, DIV, MOD, AND, SHL, SHR
3. +, -, OR, XOR
4. <, >, <=, >=, <>, =, IN
Ezt most nem kell még megtanulnia, hiszen a műveletekkel a II. tanegységben fogunk foglalkozni, ott majd hivatkozunk ezen precedencia táblázatra. (A @ műveletet nem fogjuk tanulni.) A közismert matematikai műveletek precedenciáját reméljük :-) már ismeri, egyértelmű, hogy milyen sorrendben végezzük el például a következő műveletsort:
a + b * 2
Azonos prioritás esetén a rendszer balról jobbra haladva végzi el a műveleteket. A prioritást megtörhetjük kerek zárójelek alkalmazásával. (Ellentétben a matematikában megszokottal, itt csak kerek zárójeleket használhatunk, de akármilyen mélységig egymásba ágyazhatjuk őket.)
A Pascal szigorúan típusos nyelv, minden konstansnak, változónak és így a kifejezésnek is van típusa. Szintén a II. tanegységben foglalkozunk azzal, hogy az egyes műveletek milyen típusú operandusokon végezhetők el és milyen típusú értéket eredményeznek.

Az értékadó utasítás során tehát a változóba (egész pontosan a változóhoz tartozó memóriaterületre) átkerül a jobboldalon álló kifejezés értéke. Felmerül a kérdés, hogy a két oldal típusa eltérhet-e egymástól. A Pascal ebből a szempontból is szigorú, a kifejezésnek értékadás kompatíbilisnek kell lennie a változóval. A típusok tárgyalása során (II. tanegység ) ezt is részletezzük majd.
Példaként nézzünk néhány értékadó utasítást:

X := 2;  {Itt a kifejezést egyedül egy
            számkonstans alkotja}
X := Y;  {Itt a kifejezést egyedül egy változó
alkotja}
X := X + 1;  {X régi értékéhez 1-et hozzáadunk,
               és ez lesz X új értéke}

9. Mennyi lesz X értéke a három utasítás végrehajtása után?
X := 5;
Y := 2 * ( X – 3);
X := ( ( X + 1 ) * 2 + Y ) * 3;

3.4 Adatok bevitele

A program input adatait legegyszerűbben billentyűzetről viheti be a felhasználó. Ekkor a program szemszögéből nézve olvasunk, az adat mindig a memóriába, egy változóba kerül. A Pascal nyelvben nincs input (sem output) utasítás, hanem a Turbo Pascal nyelv fejlesztői által előre elkészített rutinok, ún. eljárások végzik el ezt a feladatot. Nekünk a programunkban ezen eljárásokat kell meghívnunk. E célra a nyelv definiál egy utasítást, az eljáráshívást, ami nagyon egyszerű, hiszen csak az eljárás nevét, illetve kerek zárójelben a paramétereit kell leírnunk. A paramétereken keresztül valósul meg az eljárás és a hívó program közötti adatcsere. Nézzük konkrétan. A beolvasó eljárások a Read és a ReadLn. Paraméterként meg kell adnunk azon változó, vagy változók nevét, ahová a beolvasott adat(ok) kerül(nek). A Pascal nyelvben a paramétereket zárójelben kell elhelyeznünk. Az eljárás hívása (példaprogramunkból a leves árának bekérése):

ReadLn( Leves );

Ennél az utasításnál megáll a program végrehajtása, és arra vár, hogy begépeljünk egy értéket, mely az Enter leütésével bekerül a Leves változóba. Egyszerre több paramétert is megadhatunk vesszővel elválasztva, illetve használhatnánk a Read eljárást is, de azt tanácsoljuk, hogy az itt nem részletezett hibalehetőségek elkerülése végett mindig a ReadLn eljárást használja és csak egy paraméterrel. Ha a begépelt adat formátuma nem felel meg a beolvasandó változó típusának, akkor futási hibával megszakad a programunk. A beolvasott változók numerikus, karakteres és string (karakterlánc) típusúak lehetnek. (Ezen típusokkal a II. tanegységben fogunk foglalkozni).
Példaprogramunkban utolsó utasításként meghívtunk egy paraméter nélküli ReadLn eljárást. Ez egy Enter leütésére való várakozást jelent. Miért van itt erre szükség? Ha a fejlesztői környezet segítségével futtatjuk programunkat, akkor annak befejeződése után rögtön visszakerül a vezérlés a fejlesztői környezethez, így programunk kimenetét csak nehézkesen tudjuk megtekinteni. Ezért beiktathatunk egy várakozást, és csak az Enter leütésére fejezzük be programunkat.
Kérem olvassa el az [1] irodalomban is a két eljárás működéséről szóló fejezetet! (6.1.1 fejezet, 93., 94. old).

3.5 Adatok megjelenítése

Hasonlóan az inputhoz, a Pascal nyelvben nincs output utasítás sem, a képernyőre a Write és a WriteLn eljárások segítségével írhatunk. Az eljárások szintaktikája:

Write(k1 [,k2...])
WriteLn(k1 [,k2...])

Az eljárások az aktuális képernyő pozíciótól kezdődően kiírják a k1, k2... kifejezések értékeit. Vesszővel elválasztva több kifejezést is kiírhatunk. A szögletes zárójel a szintaktikai leírásokban azt jelenti, hogy az a rész elhagyható. A WriteLn eljárás a kiírás után sort emel. A kifejezések numerikus, karakteres, string (karakterlánc) és logikai típusúak lehetnek. (Ezen típusokkal a II. tanegységben fogunk foglalkozni). A kiírást módosíthatjuk, mezőszélességet, illetve valós kifejezés (ld. II. tanegység) esetén a tizedes jegyek számát adhatjuk meg: Write(k[:MezSzel[:Tized]]). A mezőben jobbra igazítva, illetve a megfelelő számú tizedes jegyre kerekítve jelenik meg a kifejezés értéke.
Nézzünk példákat:

{Egy adat beolvasása előtt célszerű a felhasználót tájékoztatni, hogy mit várunk tőle. Itt egy karakterlánc konstanst íratunk ki.}

Write('A savanyúság ára: ');
ReadLn(Savanyu);


{Kiír egy karakterláncot, az EgyFo nevű változó értékét, majd még egy karakterláncot.}

WriteLn('Az egy főre eső költség: ', EgyFo, 'Ft');


{Kiírjuk az X változó értékét 10 karakter szélességű mezőbe, két tizedes jegyre kerekítve.}

WriteLn(X:10:2);

{Egy üres sort ír ki (sort emel)}
WriteLn;

Nagyon lényeges, hogy pontosan lássa a következő két kiíratás közötti különbséget:
Write(X); Write('X')

Az első eljárás az X változó értékét írja ki a képernyőre, míg a második az X betűt.
Az eddigi példáinkban csak egyszerű kifejezéseket írattunk ki, amelyek csupán egy konstansból, vagy egy változóból álltak. Lehet, de talán nem célszerű összetettebb kifejezéseket is kiíratni, például:
Write( ( X + 1 ) * 2 )

10. Mi az értékadás jele a Turbo Pascal nyelvben?
A. :      B. =      C. :=      D. <-

11. Melyek lehetnek érvényes azonosítók?
A. Kamat    B. Kamat2    C. 2Kamat    D. Kamat_2    E. Kamat%
F. ÚjKamat

12 Mi a program egyes részeinek helyes sorrendje?
A. végrehajtó rész     B. deklarációs rész     C. programfej

13. Sorolja fel a változó komponenseit!

14. Mi jelenik meg a képernyőn?
X := 5;
Y := ( X + 5 ) * 2;
Y := Y + 1;
Write('X: ', X, ' Y: ', Y);


4. Keressen szintaktikai hibákat az alábbi programrészletben!
Write('Kérek egy számot');
Readln(Szam1);
Write(Kérek egy újabb számot)
Readln(Szám2);
Szam3 = Szam1 + Szám2;
WriteLn('Az eredmény: ', Szam3);


5. Mennyi lesz X és az Y értéke az utasítások végrehajtása után?
X := 10;
Y := 2 * ( X – 3) + 4 * X;
X := ( ( X - 1 ) * 2 + Y * 2 ) * 2;


6. Mi jelenik meg a képernyőn?
X := 5;
Y := 2 * ( X + 3 );
Write('X + Y = ', X + Y);

7. Írja meg annak a programnak a végrehajtó részét, amely bekér két számot, majd kiírja az összegüket és különbségüket!
8. Írja meg annak a programnak a végrehajtó részét, amely bekéri a felhasználótól, hogy melyik évben született, majd kiírja azt, hogy hány éves!

Összefoglalás

Ön e tanulási egységben megismert sok, a programozásban alapvető fogalmat, amelyeket további tanulmányi során gyakran használni fog. Csak néhányat sorolok fel közülük: algoritmus, program, folyamatábra, iteráció, fordítás, szintaktika, azonosító, változó, kifejezés, utasítás, stb.
Ön képes algoritmusokat értelmezni, egyszerű problémákra algoritmust felírni. Megtanulta, hogy a program forráskódjából a fordítás, majd a programszerkesztés után lesz futtatható program. Ezen lépések a Turbo Pascal fejlesztői környezetében elvégezhetőek, amelyet Ön alapszinten már tud használni.
Ön képes egyszerűbb Turbo Pascal programok végrehajtó részét megírni, hiszen ismer két utasításfajtát: az értékadó utasítást és az eljárás hívást, valamint a be- és kimenetet megvalósító eljárásokat. Ha bizonyos pontokon tudását még bizonytalannak érzi, az nem az Ön hibája, hiszen, mint ahogy arra többször utaltunk, ezen tanegység sok területére a továbbiakban mélyebb szinten még visszatérünk.


Ajánlott irodalom

[1] Angster Erzsébet: Programozás tankönyv I, 4KÖR Bt., 1995
[2] Hans Breuer: SH atlasz Informatika, Springr Hungarica, 1995
[3] Angster-Kertész: Turbo Pascal 6.0, Angster Erzsébet- Kertész László, 1993
[4] Angster-Kertész: Turbo Pascal ’A’..’Z’ referenciakönyv
[5] Benkő Tiborné és tsi: Programozzunk Turbo Pascal nyelven, ComputerBooks, 2001
[6] Angster-Kertész: Turbo Pascal feladatgyűjtemény, 4KÖR Bt., 1998

Gyakorló feladatok megoldása
1. Szekvencia, szelekció, iteráció.

2. A ciklus, mert ez algoritmuselem, míg a másik három algoritmus leíró eszköz.

3. 1B, 2C, 3E, 4D, 5A

4. A. kétágú szelekció      B. feltételes ciklus
C. ismert lépésszámú (növekményes, számláló) ciklus
D. szekvencia      E. egyágú elágazás

5. Kezdőfeltételes ciklust kell alkalmazni.
Vedd elő a kést!
Ha koszos, akkor
Mosd el!
Ciklus amíg van szalámi
Vágj egy szelet kenyeret!
Vajazd meg!
Tegyél rá szalámit!
Ciklus vége

6. Az alacsony szintű programozási nyelven írt kódot a hardverhez áll közel, a programozás sok munkát jelent. A magas szintű programozási nyelvek emberközeliek, hasonlítanak a beszélt nyelvre.

7. C. A. D. B

8. A. D.

9. 48

10. C

11. A, B, D

12. C, B, A

13. név (azonosító), memóriacím, érték, típus

14. X: 5 Y: 21

II. tanulási egység
Egyszerű adattípusok, szelekciók

Ön e tanulási egységben megtanulja az egyszerűbb programok készítéséhez szükséges adattípusokat, valamint a szelekciós utasításokat.
Ha Ön e tanulási egység tananyagát elsajátítja, képes lesz:
- kiválasztani a program adatainak megfelelő típust,
- deklarálni a változókat és azokkal műveleteket végezni,
- elágazást tartalmazó algoritmusokat készíteni és kódolni.

Tartalom
Bevezetés
1. Típusok jellemzése
1.1 Egész típusok
1.2 Valós típusok
1.3 Karakteres típus
1.4 Logikai típus
1.5 Felsorolt és intervallum típus
1.6 Egyszerű típusok csoportosítása
2. Szelekciók
2.1 Az If utasítás
2.2 A Case utasítás
Összefoglalás
Ajánlott irodalom
Gyakorló feladatok megoldása




Bevezetés

Ön az előző tanegységben megtanulta, hogy egy program készítésekor meg kell tervezni az adatok tárolásának, kezelésének módját, valamint a megoldás algoritmusát. Ehhez ismernie kell, hogy a kódoláshoz használt programozási nyelv milyen eszközöket nyújt az adatok kezelésére, azaz milyen adattípusok definiálását teszi lehetővé, valamint milyen utasításokat tartalmaz az egyes algoritmuselemek megvalósítására. Ezen tanulási egységben a Turbo Pascal egyszerű, más elnevezéssel skalár adattípusaival, valamint szelekciós utasításaival foglalkozunk.


1. Típusok jellemzése

Ön az I. tanegységben megtanulta, hogy a program adatokat dolgoz fel, amelyeket változókban tárol. A változóra azonosítóval hivatkozhatunk. Tartozik hozzá egy memóriaterület és rendelkezik egy típussal, amely meghatározza, hogy az adat milyen jellegű, például: egész szám, valós szám, szöveges adat, összetett, több részből álló adat. A változó típusából állapítja meg a rendszer, hogy mekkora memóriaterületet kell lefoglalnia az adat számára és hogyan kell értelmeznie a tárolt adatot. Egy típus jellemzésénél az alábbiakat fogjuk figyelembe venni:
a, felvehető értékek halmaza,
b, konstansai,
c, végezhető műveletek,
d, szabványos eljárások, függvények.

1.1 Egész típusok

A programozási nyelvek zöme a numerikus (szám) típusokon belül megkülönbözteti az egész és a valós típusokat. Ez azért van így, mert az egész számokat egyszerűbben tárolja, kezeli a rendszer, gyorsabban tud velük műveleteket végezni. Ezért ha az adat jellege lehetővé teszi, mindig egész típust válasszunk. A Pascal nyelvben vannak olyan helyzetek, ahol valós típusú szám nem is állhat (pl., for ciklus, ld. III. tanegység).

a. Felvehető értékek halmaza
A Pascal nyelvben bőséges választék áll a rendelkezésünkre egész típusokból. Az alábbi táblázat tartalmazza a típusok elnevezését, értéktartományát, valamint a tárolásakor elfoglalt területet bájtokban, azaz a méretét:

Típus Értéktartomány Tárolás
Byte 0..255 1 byte
ShortInt -128..127 1 byte
Word 0..65535 2 byte
Integer -32768..32767 2 byte
LongInt kb. -2*109..2*109 4 byte

A Byte és a Word típus esetén 8 illetve 16 biten 28 = 256 ( 000000002-tól 111111112-ig ) illetve 216 = 65536 különböző számot ábrázolhat a rendszer kettes számrendszerben. A ShortInt, az Integer és a LongInt típusokban negatív számokat is tárolhatunk. Itt az ábrázolási tartomány egyik fele kettes komplemens kódolással negatív számokat jelent. (A kettes komplemens számábrázolással nem foglalkozunk bővebben.)
Fontos megtanulnia az értéktartományokat (például integer típus: kb. –32000-től 32000-ig), és a változók típusának kiválasztásakor figyelembe venni őket. Természetesen programunk hibásan fog működni, ha futása közben egy változó értéke kilép a megengedett tartományból. (Beállítástól függően vagy leáll futási hibával, vagy ami még rosszabb, hibás értékekkel dolgozik tovább.)

Az első tanegységben már látott példát a változók deklarálására. Ön most már több típust is ismer, nézzük általánosabban a deklarálás szintaktikáját egy példán keresztül:

var Jegy, Honap: byte;
    Homerseklet: shortint;
    Fizetes, Betet: longint;

A var kulcsszó vezeti be a változók deklarációját (variable). Az azonos típusúakat vesszővel elválasztva soroljuk fel, majd kettőspont után következik a típus neve, amelyet pontosvesszővel zárunk le. Az áttekinthetőség kedvéért célszerű a különböző típusokat külön sorba írni.

b. Egész típusú konstans
A tízes számrendszerbeli, decimális egészeket a megszokott alakban használhatjuk programjainkban, pl. 25, -123. A tizenhatos számrendszerbeli, hexadecimális egészekkel is dolgozhatunk, amelyeket egy dollárjellel kell bevezetnünk, pl. $33, -$A2D6. Az A..F számjegyek esetén mindegy, hogy kis-, vagy nagybetűt írunk. (A hexadecimális számok használatára elsősorban rendszerközeli programok esetén van szükség.)

c. Végezhető műveletek
Ön az I. tanegységben megtanulta a kifejezés fogalmát, amely műveletekből (operátorokból) és operandusokból áll, és egy értéket képvisel. Nézzük először azokat a műveleteket, amelyeket egész típusú operandusokon elvégezve, az eredmény is egész típusú, azaz a művelet nem vezet ki az egész számok köréből!

+,- (előjel)
*, +, -  (a megszokott szorzás, összeadás, kivonás)

div egész osztás, pl. 13 div 3 = 4

mod maradékképzés, pl. 13 mod 3 = 1
Ne tévessze meg Önt, hogy a div, illetve mod egy-egy kis szócska, nem pedig megszokott műveleti jel! Használatukban nincs különbség pl. a *, vagy a + jelhez képest. (Sokan összekeverik őket a függvényekkel, melyekről rögtön szó lesz.) Nézzünk egy példát ezen műveletek alkalmazására:

Példa:
program Oszt;
uses Crt; {A képernyő törlést tartalmazó unit}
var Osztando, Hanyados, Maradek: byte;
begin
  ClrScr; {Képernyő törlés}
  Write('Kérem az osztandót: ');
  ReadLn(Osztando);
  Hanyados := Osztando div 3;
  Maradek := Osztando mod 3;
  WriteLn('3-mal osztva a hányados: ', Hanyados);
  WriteLn('3-mal osztva maradék: ', Maradek);
  ReadLn
end.

A program két sora némi magyarázatot igényel. A Turbo Pascal nyelvben lehetőségünk van ún. egységek (unitok) létrehozására, amelyek különböző programelemeket (eljárásokat, függvényeket, változókat, stb.) tartalmazhatnak. A unitok elemeit a programjainkban használhatjuk feltéve, ha a unit nevét a programunk elején a uses kulcsszó után megadjuk. A CRT egy, a Turbo Pascal rendszer készítői által létrehozott unit, mely a képernyővel, billentyűzettel kapcsolatos eszközöket tartalmazza (ld. IV. tanegység). Mi ezen unit ClrScr (Clear Screen - képernyőtörlés) eljárását használtuk programunkban.

A következő, bitenkénti műveleteket csak megemlítjük, kurzusunkban a továbbiakban nem alkalmazzuk őket. Hasonlóan a hexadecimális számokhoz a rendszerközeli programozásban gyakori az alkalmazásunk.
not bitenkénti negálás,
   pl not 28 = -29 ; (not 00011100 = 11100011)
and bitenkénti és,
   pl. 5 and 6 = 4; (00000101 and 00000110 = 00000100)
or bitenkénti vagy,
   pl. 5 or 6 = 7; (00000101 or 00000110 = 00000111)
xor bitenkénti kizáró vagy,
   pl. 5 xor 6 = 3; (00000101 and 00000110 = 00000011)
shl bitenkénti eltolás balra,
   pl. 5 shl 3= 40; (00000101 shl 3 = 00101000)
shr bitenkénti eltolás jobbra,
   pl. 5 shr 2= 1; (00000101 shr 2= 00000001)
A not, and, or, xor operátorokkal, mint fontos, gyakran használt műveletekkel a logikai típus esetén (1.3 fejezet) még fog találkozni,

Az eredmény kivezet az egész számok köréből:
/ az eredmény mindig valós (bár 3 az értéke, 6/2-t már nem egész számként kezeli a rendszer)
<, >, <=, >=, =, <> relációs műveletek, az eredmény logikai típusú: igaz, vagy hamis (ld. 1.3 fejezet). Vigyázzon, hogy az egyenlőség vizsgálatot (=) ne keverje össze az értékadás jelével (:=)!
in halmaz elemvizsgálat, logikai eredmény (nem tárgya kurzusunknak)

d. Fontosabb szabványos eljárások, függvények
Ön már megtanulta és használta a beolvasás (ReadLn) és kiírás (Write, WriteLn) szabványos eljárásait. Említettük, hogy az eljárások olyan alprogramok, amelyek valamilyen tevékenységet, utasítássorozatot hajtanak végre. A szabványos, vagy standard eljárásokat a Turbo Pascal rendszer készítői bocsátják a rendelkezésünkre, részei a nyelvnek. Az eljárások aktivizálását hívásnak nevezzük, amely a Pascalban nagyon egyszerű, csak a nevüket kell leírnunk, és a megfelelő paraméterekkel ellátnunk. Ugye emlékszik, ez az egyik egyszerű utasítása a Pascal nyelvnek: az eljáráshívó utasítás.
Szintén alprogramok a függvények, de van néhány jelentős különbség az eljárásokhoz képest. Melyek ezek? A függvény elsődleges feladata, hogy bemenő paramétereiből egy értéket előállítson elő. Ezt az értéket a függvény visszatérési értékének nevezzük. Minden függvénynek meghatározott számú és típusú paramétere van. Megjegyezzük, hogy létezik olyan függvény, amelynek nincs paramétere. Később (V. tanegység) még részletesen foglalkozunk a paraméterek kérdésével, de általánosságban elmondható, hogy a függvények paramétere kifejezés lehet, tehát egy függvénybe ágyazhatunk akár újabb függvényt is ( pl.: Sin( x + Abs(Y)) ). A függvény hívása is eltér az eljárásétól, hiszen egy kifejezésben kell meghívnunk, ahol az általa szolgáltatott érték operandusként szerepel. ( Például A := 5 + Abs(B) ). Lapozzon vissza az I. tanegység 3.3 fejezetében a kifejezés fogalmához, ahol már említettük, hogy mik szerepelhetnek operandusként.

Nézzük az egész típussal kapcsolatos fontosabb standard eljárásokat, függvényeket! Ahol egész paraméternek kell állnia, ott I (esetleg N), ahol pedig valós paraméternek, ott R változónevet használtunk.

Eljárások:

Inc(I, N)
I értékét növeli N-el (inkrementálja), N elhagyható, abban az esetben 1-gyel növel.

Pl.:
A := 5;
Inc(A, 2);   {ugyanaz, mint A := A + 2;}
             {A értéke 7 lesz}

Dec(I, N)
I értékét csökkenti N-el (dekrementálja), N elhagyható, abban az esetben 1-gyel csökkent.
Pl.:
A := 5;
Dec(A, 2);   {ugyanaz, mint A := A - 2;}
             {A értéke 3 lesz}

Str, Val
A string (karakterlánc) típus tárgyalásakor (IV. tanegység) foglalkozunk bővebben velük.

Randomize
Paraméter nélküli eljárás, olyan programban, ahol véletlen számokat állítunk elő a Random függvénnyel, általában egyszer meg kell hívnunk. Inicializálja (előkészíti) a véletlen szám generátort.

Függvények:

Abs(I)
Abszolút érték függvény.

Sqr(I)
A paraméterének a négyzetét állítja elő. Megegyezik I * I-vel, ha a paramétere egy hosszabb kifejezés, akkor célszerű használni, pl.:
A := Sqr(B + 2 * Abs(C))

Trunc(R)
Úgynevezett konverziós függvény, valós paraméterét egész típusúvá konvertálja a törtrész levágásával.
X := 4.56;
A := Trunc(X)    {A értéke 4 lesz, típusa egész}

Round(R)
Úgynevezett konverziós függvény, valós paraméterét egész típusúvá konvertálja kerekítéssel.
X := 4.56;
A := Round(X)    {A értéke 5 lesz, típusa egész}

Random(I)
Egy véletlen egész számot állít elő a 0..I-1 intervallumban. (Ld. még Randomize és valós típusok függvényei.)
A := Random(90) + 1;
{az intervallumot 1-gyel eltoltuk, így 1..90}
Writeln('Az első nyerőszám: ', A);


Példa: Határozzuk meg egy háromjegyű szám számjegyeinek az összegét!
program szamjegy;
uses Crt;
var Szam, Osszeg: integer;
begin
  ClrScr;
  Write('Kérek egy háromjegyű számot: ');
  ReadLn(Szam);
  Osszeg := 0;
  {A százas helyiérték hozzáadása az összeghez}
  Osszeg := Osszeg + Szam div 100;
  {A százas helyiérték leválasztása}
  Szam := Szam mod 100;
  {A tízes helyiérték hozzáadása az összeghez}
  Osszeg := Osszeg + Szam div 10;
  {A tízes helyiérték leválasztása}
  Szam := Szam mod 10;
  {Az egyes helyiérték hozzáadása az összeghez}
  Osszeg := Osszeg + Szam;
  WriteLn('A számjegyek összege: ', osszeg);
  ReadLn
end.

1.2 Valós típusok

Természetesen a programozási feladatok nagy részét nem lehet megoldani csak egész számok használatával. A Turbo Pascal nyelv több valós típussal is rendelkezik, mi csak az alaptípusnak tekinthető Real típussal foglalkozunk. Menjünk sorra a Real típus jellemzőin.

a. Felvehető értékek halmaza
A Real típusú változók 6 bájtot foglalnak el a memóriában. Ábrázolásuk, kezelésük bonyolultabb módon történik, mint az egész típusok esetében. A rendszer a matematikai normál alakhoz hasonló, lebegőpontos formában tárolja el a számot (Bővebben lásd: [1] 114-115. old.). A legkisebb abszolút értékű valós szám 2,9*10-39 , a legnagyobb pedig 1,7*1038 . A pontosság maximálisan 11-12 decimális számjegy. (Ennyi értékes számjegye van egy valós számnak, a többi jegyet nem ábrázolja a rendszer, pl. a 1234567890,123456 számból az aláhúzott jegyek elvesznek.)

b. Valós típusú konstans
Tizedespontot kell alkalmaznunk (nem vesszőt), nagy, illetve kis abszolút értékű számok esetén használhatjuk a normál alakú formát.
Például:  5.12, -123.2313, 12.54E6 (12,54*106), 21.12E-5 (21,12*10-5)

c. Végezhető műveletek
Az eredmény is valós típusú: +,- (előjel), *, /, +, -
Az eredmény logikai típusú: <, >, <=, >=, =, <>
(Vigyázzunk, nincs div és mod!)

d. Fontosabb szabványos eljárások, függvények
Már az egész típus tárgyalásánál tárgyaltuk az Abs, Sqr, Round, Trunc függvényeket, illetve az Str, Val, Randomize eljárásokat.
Újabb függvények, amelyek mind valós visszatérési értékkel rendelkeznek:

Sqrt(R)
Egy szám négyzetgyökét adja vissza.

x1 := (-b +  Sqrt( b * b - 4* a * c)) / (2 * a)


Sin(R), Cos(R)
Egy radiánban megadott szög szinuszát, illetve koszinuszát adja vissza.

ArcTan(R)
Arkusztangens függvény, visszatérési értéke radiánban értendő.

Pi
Paraméter nélküli függvény, a pi értékét adja vissza.

Alfa := 30;
Radian := pi * 30 / 180;
X := Sin(Radian); {X értéke 0.5 lesz}

Exp(R)
Exponenciális függvény, eR – ent adja vissza. (e  2,71)

Ln(R)
Paraméterének a természetes alapú logaritmusát adja vissza.

Int(R)
A paraméterének az egész részét adja vissza, de valós típusúként kezelve.

X := Int(123.456); { 123.0 }

Frac(R)
A paraméterének a tört részét adja vissza.

X := Frac(123.456); { 0.456 }

Random
Már találkoztunk vele az egész típusoknál, ha paraméter nélkül hívjuk meg, akkor egy valós véletlen számot állít elő a 0..1 intervallumban.


Az I. tanegység 3.3 fejezetében már említettük, hagy az értékadó utasításban (változó := kifejezés ) a kifejezésnek értékadás kompatíbilisnek kell lennie a változóval. Nézzük, hogy a numerikus típusok körében ez mit jelent! Ön mit gondol, melyik értékadás helyes az alábbiak közül, ha az A változó egész, X pedig valós típusú?

A := X;
X := A;

Biztos vagyok benne, hogy helyesen gondolta, a második értékadó utasítás a helyes, azaz egy „bővebb” típusú változónak adhatunk értéket egy „szűkebb” típusú kifejezés (ami a példánkban egyszerűen egy változó) által. Az első utasításnál a fordító szintaktikai hibát jelez. Hasonló módon szintaktikai hibás a következő programsor: A:=6/3, hiszen az osztás (/) kivezet az egész számok köréből.

Nézzünk egy egyszerű példát a real típus alkalmazására!

Példa: Olvassuk be egy kör sugarát, majd számítsuk ki a kerületét és a területét!
program Kor;
uses Crt;
var R, Ker, Ter: real;
begin
  ClrScr;
  Write('Kérem a kör sugarát: ');
  ReadLn(R);
  Ker := 2 * R * Pi;
  Ter := R * R * Pi;
  WriteLn('A kör kerülete:', Ker:10:2);
  WriteLn('A kör területe:', Ter:10:2);
  ReadLn
end.

Ha egy valós szám kiíratásakor nem adjuk meg a mezőszélességet és a tizedes jegyek számát (Ker:10:2), akkor a számot a kevésbé áttekinthető normálalakban írja ki a rendszer.


1.3 Karakteres típus

Nagyon gyakori, amikor egy programnak nem numerikus adatokat kell feldolgoznia, hanem szöveget. Gondoljon csak a leggyakrabban használt számítógépes irodai alkalmazásokra, a szövegszerkesztőkre. A szöveges típusú adatok kezeléséhez a Pascal nyelv a karakter (Char) és a karakterlánc (String) típust nyújtja. Ön a karakterlánc típusról a IV. tanegységben fog tanulni. Most nézzük a Char típus leírását!

a. Felvehető értékek halmaza
A Char 1 bájtos típus, tehát 28 = 256 különböző értéknek, az ASCII kódrendszer 256 elemének a tárolására képes. A karakter típusú változó egy ASCII kódot tartalmaz. Pl. ha a változóhoz tartozó memóriarekesz értéke 65, akkor, mivel változónk típusa Char, ezt a rendszer 'A' betűként értelmezi.

b. Konstansok
Ön már az I. tanegység 3.1 fejezetében megtanulta, hogy a Pascal nyelv a karakterlánc konstansként értelmezi az aposztrófok közötti tetszőleges ASCII karakterekből álló szöveget. A karakteres konstans abban különbözik ettől, hogy csak egyetlen karaktert tartalmazhat, pl.: 'A', '*', '4'. Egy másik formája a Char konstansnak, ha az ASCII kód elé közvetlenül a kettőskereszt karaktert írjuk: #65, a 65-ös ASCII kódú karaktert, azaz 'A'-t jelenti. Gyakran hivatkozunk így az Escape - #27 karakterre.

c. Műveletek
A Char típus esetén nincs olyan művelet, amelynek az eredménye is karakteres típusú lenne.
Mint a számoknál, itt is értelmezi a nyelv a relációs műveleteket: <, >, <=, >=, =, <>. Az eredmény természetesen logikai típusú, amit a karakter ASCII kódja határoz meg. Pl. 'A' < 'B' igaz, 'A' = 'a' hamis.
Alkalmazható még az in művelet, amellyel kurzusunkban nem foglalkozunk.

d. Fontosabb szabványos eljárások, függvények

Eljárások:

Inc(K, N), Dec(K, N)
Ön már találkozott ezekkel az eljárásokkal az egész típusoknál. Működésük itt is hasonló, a K karakter ASCII kódját növelik, illetve csökkentik N-nel, N elmaradhat, akkor eggyel növelnek, illetve csökkentnek.

K := 'A';
Inc(K, 2);  {K értéke a 'C' karakter lesz}


Függvények:

Ord(K)
A karakter ASCII kódját adja vissza.

WriteLn('A ? ASCII kódja: ', Ord('?'));

Chr(I)
Az Ord függvény inverzének tekinthető, az ASCII kódnak megfelelő karaktert adja vissza.

WriteLn('100-as ASCII kódú karakter: ',
         Chr(100));

UpCase(K)
Ha a paramétere egy kisbetű, akkor a nagybetűs változatával tér vissza, egyébként nincs hatása.

K := 'a';
K := UpCase(K); {K értéke az A betű lesz}

Pred(K)
A K-t megelőző karakterrel tér vissza.

Succ(K)
A K-t követő karakterrel tér vissza.

Itt is megjegyeznénk, hogy az eddigi függvényeink paraméterei nem csak változók, hanem kifejezések lehetnek. (A kifejezés állhat egyetlen változóból is.) Például: K := UpCase(Chr(100+A)). Itt az UpCase függvény paramétere egy olyan kifejezés, amely egyetlen függvényhívásból áll, a Chr függvény paramétere pedig egy kétoperandusú kifejezés.

Példa: Kérjünk be egy nagybetűt, majd írjuk ki a neki megfelelő kisbetűt!
program Kisbetu;
uses Crt;
var Betu: char;
begin
  ClrScr;
  Write('Kérek egy nagybetűt: ');
  ReadLn(Betu);
  Inc(Betu, 32);  {*}
  WriteLn(Betu);
  ReadLn
end.

A megoldásban kihasználtuk, hogy a kis és nagybetűk kódja között 32 a különbség. A *-gal megjelölt sor helyett a következőt is írhattuk volna: Betu := Chr( Ord(Betu) + 32 );.
Értelmezze ezt az utasítást!

1.4 Logikai típus

Ön az I. tanegységben már megtanulta, hogy a szelekciók és iterációk esetén feltételek igaz, vagy hamis értékétől függhet az algoritmus további menete. Ezek a feltételek logikai típusú kifejezések. A Pascal nyelvben logikai típusú változók deklarálására is van lehetőség, a típus neve Boolean, mely elnevezés George Boole 19. századi angol matematikusra utal.

a. Értéktartomány
Egy logikai típusú változó két értéket vehet fel: igaz vagy hamis. Ábrázolása egy bájton történik (akár egy bit is elég lenne). Ha a bájt értéke 0, akkor a logikai típusúként értelmezett érték hamis, nullától eltérő érték esetén pedig igaz.

b. Konstansok
A két logikai értéknek megfelel két előredefiniált konstans: True (igaz), False (hamis)

c. Műveletek
Az eddig tanult típusok esetén már láttuk, hogy a relációs operátorok (valamint az in halmazművelet) logikai értéket állítanak elő. Logikai típusú operandusokra alkalmazhatók a logikai műveletek (operátorok), melyeknek az eredménye is boolean. A logikai műveletek kiértékelését igazságtáblával szokták megadni. Nézzük a Pascal nyelv logikai műveleteit:

not (nem)

A not A
Hamis Igaz
Igaz Hamis

and (és) – akkor ad igaz értéket, ha mind a két operandus igaz

A B A and B
Hamis Hamis Hamis
Hamis Igaz Hamis
Igaz Hamis Hamis
Igaz Igaz Igaz

or (vagy) – akkor ad igaz értéket, ha legalább az egyik operandus igaz

A B A or B
Hamis Hamis Hamis
Hamis Igaz Igaz
Igaz Hamis Igaz
Igaz Igaz Igaz

xor (kizáró vagy) – akkor ad igaz értéket, ha pontosan az egyik operandus igaz

A B A xor B
Hamis Hamis Hamis
Hamis Igaz Igaz
Igaz Hamis Igaz
Igaz Igaz Hamis

A bonyolultabb logikai kifejezések több relációs és logikai operátort is tartalmazhatnak, ezért nagyon fontos ismernie ezen műveletek precedenciáját. Kérem, lapozzon vissza az I. tanegység 3.3 fejezetéhez, és nézze meg, hogy a fenti műveletek hol helyezkednek el a Pascal nyelv operátorainak precedencia táblázatában. Ezt láthatja ott: a not, mint egy operandusú művelet, a legerősebb, az and a szorzás (multiplikatív), míg az or és a xor az összeadás jellegű (additív) műveletek szintjén helyezkedik el. Ez könnyen megjegyezhető, a legtöbb programozási nyelv esetén így definiálja a logikai műveletek hierarchiáját. Arra viszont ügyelnie kell, hogy a relációs műveletek a hierarchia legalján helyezkednek el. Hogy mit jelent ez praktikusan, nézzük meg az alábbi példán. Legyen B egy logikai változó, amely akkor igaz, ha az X egész szám 10 és 20 közé esik. Ezt a matematikában így írnánk:

10 < X < 20    HIBÁS!

Ezt a Pascal nem értelmezi, próbálkozzunk másképp:

10 < X and X < 20    HIBÁS!

Ez is szintaktikai hibás, mivel a rendszer először a nagyobb precedenciájú and műveletet értékeli ki.

(10 < X) and (X < 20)    JÓ!

Itt a jó megoldás, először a két zárójelben lévő relációt értékeli ki a rendszer, majd az így kapott logikai értékekre végzi el az and műveletet. Tehát a relációs műveletek alacsony precedenciájának az a következménye, hogy az összetett logikai kifejezésekben zárójelbe kell tennünk őket.

Még néhány példán gyakoroljuk a logikai kifejezések használatát! (logikai változók):
var B1, B2, B3, B4: boolean;
    X, Y, Z: byte;
...

X := 5; Y := 10; Z := 15;

B1 := X > Y; {hamis}

B2 := (X > Y) or (Z > Y); {igaz}

B3 := not (X > Y) and (B2 or (Z < 10)); {igaz}







B4:= B2 or B1 and not B3; {igaz}


d. Logikai értéket előállító függvények

Odd(I)
Párosság vizsgálat. Visszatérési értéke igaz, ha I páratlan.

I := 6;
B1 := Odd(I); {hamis}
B2 := I mod 2 <> 0; {hamis}

Eof, Eoln, nem foglalkozunk a kurzus keretében velük.

Lényeges tudnia, hogy az eddig tanult típusok közül (egész, valós, karakter, logikai) a logikai az egyetlen, amit nem lehet a Read, illetve ReadLn eljárásokkal a billentyűzetről beolvasni. Ellenben egy logikai kifejezést ki tudunk íratni, a TRUE, vagy a FALSE szócska jelenik meg a képernyőn.


1.5 Felsorolt és intervallum típus

Minkét típus esetén közös, hogy a felvehető értékek halmazát a programozó adhatja meg.
A felsorolt típus értékei azonosítók lehetnek, melyeket a deklaráláskor fel kell sorolnunk. Például:

var Tantargy: (Magyar, Tori, Matek, Tesi);

A változó (Tantárgy) által felvehető értékeket zárójelben, vesszővel elválasztva adjuk meg. Az értékekhez a rendszer egy sorszámot rendel, 0-tól kezdődően.
Az intervallum típust egy már létező típus intervallumaként, a felső és alsó határával adhatjuk meg. Például:

var Szamjegy: '0'.. '9';
    Honap: 1..12;

Lényeges, hogy az intervallum határai közé pontosan két pontot írjunk.
Részletesebben nem foglalkozunk ezen két típussal. Kérjük, hogy kiegészítő tananyagként olvassa el az [1] irodalom ide vonatkozó rövid fejezeteit (7.5, 7.5 fejezet, 122-125. old.)!

1.6 Egyszerű típusok csoportosítása

Az egyszerű típusokat két csoportba sorolhatjuk.

I. Sorszámozott típusok
A típus minden értékéhez tudunk sorszámot rendelni. Ide tartoznak az
- egész típusok (byte, shortint, word, integer, longint),
- karakteres típus (char)
- logikai típus (boolean)
- felsorolt típus
- intervallum típus

II. Nem sorszámozott típusok: a valós típusok (real), hiszen felvehető értékeit nem tudjuk „besorszámozni”. A későbbiekben tanult típusok közül egyik sem lesz sorszámozott.

A továbbiakban még több ízben hivatkozunk ezen csoportosításra, hiszen a nyelv bizonyos elemeiben csak sorszámozott típust használhatunk (pl.: for ciklus, case szelekció, tömbök,).



Összefoglalva az egyszerű típusokról tanultakat:

Az ön által eddig tanult típusok a valós (real) kivételével mind sorszámozottak. Ön megtanulta, hogy az egyes típusok milyen értékeket vehetnek fel, hogyan használhat konstansokat, milyen műveleteket végezhet az egyes típusokkal és mely fontosabb standard (előre elkészített, és a rendelkezésünkre bocsátott) függvényeket, eljárásokat használhat programjaiban. Néhány lényeges pontra, hibaforrásra hívnám fel a figyelmét.
- A div és mod az egész osztás műveletei (nem függvények, vagy eljárások), csak egész számokra alkalmazhatók.
- A programban használt összes változót deklarálnia kell a var kulcsszó után.
- A / művelet eredmény mindig valós.
- Az értékadás kompatíbilitás szerint egy valós változó kaphat egész értéket, viszont fordítva ez szintaktikai hibát jelent.

Kérem oldja meg a gyakorló feladatokat!

1. Hány bájtosak az alábbi típusok: byte, word, integer, char, real?

2. Melyek azok az egész típusok, amelyek felvehetik az alábbi értékeket? A legkisebb méretű típust, vagy típusokat adja meg!
A. 112   B. -2640   C. 50000   D. –12. E. 100000

3. Keressen szintaktikai hibákat az alábbi programrészletben!
program hibas;
var a,b:integer;
    x,y:real;
begin
  readln(b);
  a:=b/3;
  x:=a+b;
  y:=x mod 2;
  writeln(a,' ',x,' ',y);
end.

4. Döntse el, hogy az alábbi állítások igazak-e!
A. Az értékadó utasítás jobboldalán mindig egy kifejezés áll.
B. A fenntartott szavakat másra nem használhatom, csak amire a nyelv őket definiálja.
C. Div egy függvény, mely két szám hányadosát állítja elő.
D. ReadLn egy utasítás.
E. Egy azonosító csak betűvel és számjeggyel kezdődhet.

5. Állítsa csökkenő precedencia szerint sorrendbe a következő operátorokat: OR, DIV, <>, NOT !

6. Párosítsa össze a típusokat a kifejezésekkel! (A és B egész)
A. Logikai 1. B + A div 2
B. Karakteres 2. B + A / 2
C. Egész szám 3. chr( B + A div 2 )
D. Valós szám 4. A div 2 = 0

7. Mi az alábbi kifejezés értéke:
59 Mod 10 Div 4

A. 9     B. 2     C. 3     D. 2.25

8. Adottak a következő deklarációk:
Var
  C:Char;
  A,B:Byte;

Mely értékadás(ok) helyes(ek)?
A. C := '5';   B. B := 256;   C. A := B+C;   D. A,B := 0;

9. A, B, és C mely értékeire ad igaz eredményt a következő kifejezés?
(A>B) And (B<C) And Not(A>C)

A. A=2, B=5, C=9
B. A=5, B=9, C=2
C. A=9, B=2, C=5
D. A=5, B=2, C=9

10. Adottak a következő deklarációk:
Var
  I,J:Word;
  B1,B2,B3:Boolean;

Melyek a szintaktikusan helyes logikai kifejezések?
A. I=5 And J=5
B. B1 And B2=B3
C. Not(I>J) Or B2
D. B1 And Not(I And J)


1. Adottak a következő deklarációk:
Var
  A:Byte;
  B:Char;
  C:Boolean;

Mely értékadások helyesek?
a. A:=B+4;      b. C:=A>10;      c. B:=A;      d. B:='A';

2. Mennyi lesz az A változó értéke az programrészlet végén?
C := 'a';
Inc(C, 5);
C := UpCase(C);
C := Succ(C);
A := Ord(C) – Ord('A');

3. Milyen bemenő értékek esetén működik helyesen az alábbi programocska:
  var a,b,c: shortint;
  begin
   write('Kérek egy számot: ');
   readln(a);
   b:=10;
   c:=a*b;
   writeln('A szám tízszerese: ',c);
  end.

4. Írjon olyan logikai kifejezést, amely igaz, ha az X változó a –5 …+5 nyílt intervallumba esik!

5. Ha I értéke 5 és B értéke False, a következő kifejezések közül melyiknek igaz az értéke?
A. (I<5) And B     B. (I<=5) Or B
C. (I<=5) And B    D. (I<5) Or B

6. Készítse el az I. tanegység 7. és 8. feladatának teljes programját!

7. A Szamjegy programban a magasabb helyiértékek irányából adtuk össze a számjegyeket. Alakítsa át úgy a programot, hogy ez az alacsonyabb helyiértékek irányából történjen!

8. Írjon programot, amely bekéri a kifizetendő pénzösszeget, amely 1000-el osztható és 50000-nél kisebb, (ezt nem kell ellenőriznie), majd kiírja, hogy hány darab 20000-es, 10000-es, 5000-es, 2000-es és 1000-es bankjegyet kell kifizetnünk!

9. Írjon programot, amely bekéri egy derékszögű háromszög egyik hegyesszögének és az átfogójának az értékét, majd kiírja a befogók hosszát!

10. Írjon programot, amely beolvas egy karaktert, majd kiírja az ASCII táblában előtte és utána lévő karaktert!

11. Írjon programot, amely beolvas egy számot (N-t, melyről feltételezhetjük, hogy pozitív, egész és 27-nél kisebb), majd kiírja a nagybetűs angol ABC N. betűjét!

12. Írjon programot, amely beolvas két számot, majd kiírja a számtani és mértani közepüket!

2. Szelekciók
Ön az I. tanegységben megtanulta, hogy algoritmusainkat három vezérlőszerkezet felhasználásával (szekvencia, szelekció, iteráció) tudjuk felépíteni. Az eddig készített programjaink csak egyszerű utasítássorozatot, azaz szekvenciát tartalmaztak. Ezzel a programozási feladatoknak csak egy szűk körét tudjuk megoldani. Gyakran van szükségünk arra, hogy programunk futása közben reagáljon bizonyos körülményekre, azaz feltételektől függően más és más úton haladjon tovább, elágazzon. A Pascal nyelv az elágazások (szelekciók) megvalósítására két feltételes utasítást tartalmaz (if és case).

2.1 Az If utasítás

Az if-then-else utasítás kétirányú feltételes elágazást hajt végre. Az utasítás szintaktikája:

if feltétel then utasítás1 [else utasítás2]

ahol a feltétel egy logikai kifejezés. Ha a feltétel igaz, akkor az utasítás1 hajtódik végre, egyébként az utasítás2 (kétágú szelekció). Az else ág elhagyható, ilyenkor ha a feltétel hamis, akkor az utasítás1 kimarad, a program a következő utasítással folytatódik (egyágú szelekció). Ezen vezérlőszerkezetek mondatszerű leírását és folyamatábráját megtalálhatja az I. tanegység 1.2 fejezetében. Kérem ismételten tanulmányozza az ott leírtakat!
Láthatjuk, hogy az if-then-else utasítás maga is tartalmaz utasításokat, ezért ezen utasítás már a strukturált utasítások csoportjába tartozik, szemben az eddig tanult egyszerű utasításokkal (értékadó utasítás, eljárás hívás).
Nézzünk néhány példát:

Példa:
1.
 ReadLn(Oszto);
 ReadLn(Osztando);
 if Oszto <> 0 then
   Hanyados := Osztando / Oszto;

Az osztást csak akkor hajtjuk végre, ha az Osztando nem 0.
2.
 ReadLn(Oszto);
 ReadLn(Osztando);
 if Oszto <> 0 then
   Hanyados := Osztando / Oszto
 else
   WriteLn('0-val való osztás, nincs értelmezve.');

Kétágúra bővítettük az elágazást, ha az Osztó értéke 0 (else ág), akkor egy hibaüzenetet írunk a felhasználónak. Vigyázzon, az else kulcsszó elé nem szabad pontosvesszőt tenni, mert ezzel azt jeleznénk a fordítóprogramnak, hogy lezártuk az if utasítást, azaz nem következik else ág. Mindkét példa esetén láthatjuk, hogy az egyes ágakban lévő utasításokat kissé beljebb írtuk. Ez természetesen nem kötelező, de programunkat olvashatóbbá, áttekinthetőbbé teszi. Ez az írásmód azt is tükrözi, hogy az if-then-else már egy strukturált utasítás.

Példa
Egészítsük ki az karakteres típusnál látott nagybetűt kisbetűvé alakító programunkat úgy, hogy ha a bekért karakter nem nagybetű, akkor ne változtassa meg!
program Kisbetu2;
uses Crt;
var Kar: char;
begin
  ClrScr;
  Write('Kérek egy karaktert: ');
  ReadLn(Kar);
  if (Kar >= 'A') and (Kar <= 'Z') then
    Inc(Kar, 32);
  WriteLn(Kar);
  ReadLn
end.

Gyakran van szükség arra, hogy egy ágon több utasítást is végrehajtsunk, sőt az egyes ágakon belül tetszőleges bonyolultságú programrészletek elhelyezkedhetnek (újabb elágazások, iterációk). Azonban a nyelv szintaktikája egy ágban csak egy utasítást engedélyez. Ezt a problémát oldja fel a Pascal a begin - end, úgynevezett összetett (más néven szekvencia vagy blokk) utasítással. A begin és end kulcsszavak közé írt tetszőleges utasításokat (szekvenciát) elhelyezhetjük ott, ahol a nyelvtan szerint csak egy utasítás állhat. Gyakran nevezik a begin-end párt utasítás zárójelnek is. Vigyázzunk, hogy csak a program utolsó end-je után kell pontot tennünk. Ahogy a kurzus során halad előre, a programjaiban egyre több begin-end pár fog előfordulni.

Példa
Ha az A változó értéke nagyobb, mint a B változóé, akkor cseréljük fel az értéküket!
program Csere;
uses Crt;
var A, B, Id: byte;
begin
  ClrScr;
  Write('A: '); ReadLn(A);
  Write('B: '); ReadLn(B);
  if A > B then
    begin
      Id := A;
      A := B;
      B := Id;
      WriteLn('A: ', A, ' B: ', B)
    end
  else
      WriteLn('Nem történt csere.');
  ReadLn
End.

Könnyen belátható, hogy két változó értékének cseréjéhez egy harmadik, ideiglenes (példánkban Id) változóra is szükség van. Az else elé, azaz az igaz ág end-je mögé most sem tettünk pontosvesszőt, mivel szintaktikailag hibás lenne. A WriteLn('A: ', A, ' B: ', B) eljáráshívó utasítás mögé tehettünk volna pontosvesszőt, az nem lett volna hiba, de mivel nem következik utána utasítás, hanem egy end, ezért nem szükséges az elválasztó pontosvessző (hasonlóan a program utolsó utasításához (ReadLn).

Az if utasítás általános szintaktikájában akár az utasítás1 vagy az utasítás2 is lehet újabb If utasítás, és ezáltal többirányú elágazást is megvalósíthatunk. Ezt a szerkezetet egymásba ágyazásnak is nevezik.

Nézzünk példákat:

Példa: Egy számról írjuk ki, hogy pozitív, negatív, vagy nulla!
Ez egy három irányú elágazás, nézzük először az algoritmus folyamatábráját:















A szelekció hamis ágába, azaz az else ágába ágyaztunk egy újabb szelekciót, azaz egy if utasítást. Így három felé ágazott a programunk. Ezt a megoldást if then else if  szerkezetnek is nevezik. Akkor alkalmazható, ha az egyes esetek (ágak) egymást kizárva teljesülhetnek, tehát mindig csak egy. Példánk programkódja:

program Elojel;
uses Crt;
var X: real;
begin
  ClrScr;
  Write('X: '); ReadLn(X);
  if X > 0 then
    WriteLn('Pozitív')
  else
   if X < 0 then
     WriteLn('Negatív')
   else
     Writeln('Nulla');
  ReadLn
end.

A következő példa megoldása is az if then else if  szerkezet használatával történik, itt az írásmóddal, az egyes utasítások beljebb kezdésével nem az egymásba ágyazást hangsúlyozzuk, hanem, hogy egymással egyenértékű esetek, egymást kizárva fordulhatnak elő.

Példa: Olvassuk be a hónap sorszámát, majd írjuk ki, hogy ez mely évszakba tartozik.
program Honap;
uses Crt;
var Ho: byte;
begin
  ClrScr;
  Write('Hónap: '); ReadLn(Ho);
  if (Ho = 1) or (Ho = 2) or (Ho = 12) then
    WriteLn('Tél')
  else if (Ho >= 3) and (Ho <= 5) then
    WriteLn('Tavasz')
  else if (Ho >= 6) and (Ho <= 8) then
    WriteLn('Nyár')
  else if (Ho >= 9) and (Ho <= 11) then
    WriteLn('Ősz')
  else
    WriteLn('Nincs ilyen hónap');
  ReadLn
end.

A következő példában egy if utasítás igaz ágába ágyaztunk egy újabb if then else utasítást. Ebben az esetben a Pascalnak az a nyelvtani szabálya érvényesül, hogy az else mindig a hozzá legutóbbi then párja!

if A > 0 then
  if Not Odd(A) then
    Writeln('Pozitív, páros')
  else
    Writeln('Pozitív, páratlan');

Ha azt akarjuk, hogy az else ág az első then-hez tartozzon, akkor így kell eljárnunk.

if A > 0 then
 begin
  if Not Odd(A) then
    Writeln('Pozitív, páros')
 end
else
 Writeln('Nem pozitív');

A fenti példákból is kitűnik, hogy alaposan át kell gondolnunk az algoritmust, ha több irányú elágazást kell egymásba ágyazott ciklusokkal megvalósítanunk.

2.2 A Case utasítás

A honap nevű program esetén egy öt ágú szelekciót láttunk, sőt előfordulhat, hogy még több esettel van dolgunk az elágazás során. A Pascal másik szelekciós utasításával, a case utasítással könnyen megoldhatjuk a programunk többirányú elágaztatását.
A case utasítás általános szintaktikája:

case kifejezés of
   érték1: utasítás1;
   érték2: utasítás2;
   ...
   értékN: utasításN;
   [else
      különben_utasítás]
end;

A case kulcsszó után álló kifejezés (az utasítás szelektora) csak sorszámozott típusú lehet (gyakori esetben egy változó). Az érték1, érték2,... értékN ún. esetkonstansok a szelektor lehetséges értékeit jelölik. Ha a kifejezés értéke megegyezik valamelyik konstans értékével, akkor a program végrehajtása a konstans után kettősponttal megadott utasítással folytatódik tovább. Ha a szelektor értéke egyik konstanssal sem egyezik meg, akkor a program az else ág utáni utasítással, vagy annak elhagyása esetén a case utáni utasítással folytatódik. A case szerkezetet az end kulcsszóval kell lezárnunk. (Ez az első olyan eset, ahol az end-nek nincs begin párja.)
Egy ághoz több értéket is felsorolhatunk vesszővel elválasztva, illetve intervallumot (intervallumokat) is megadhatunk. Egy intervallum jelölésével Ön már találkozott az intervallum típus esetén, kérem lapozzon vissza, hiszen az ott használt alak általános a Pascal nyelvben. Ha egy esethez, ághoz több utasítás is tartozik, akkor azokat a begin-end kulcsszavak közé kell zárnunk, mint ahogy azt az if utasításnál már láttuk. Természetesen az egyes ágakban bármilyen utasítások állhatnak.
Első példaként oldjuk meg case szelekcióval is a honap programot!

Példa
program Honap2;
uses Crt;
var Ho: byte;
begin
  ClrScr;
  Write('Hónap: '); ReadLn(Ho);
  case Ho of
    1, 2, 12: WriteLn('Tél');
    3..5:     WriteLn('Tavasz');
    6..8:     WriteLn('Nyár');
    9..11:    WriteLn('Ősz');
    else      WriteLn('Nincs ilyen hónap');
  end;
  ReadLn
end.

Azt hiszem Ön is egyetért velem, hogy ebben a formában áttekinthetőbb a program, mint az if then else if szerkezet használatával. Viszont itt hívnám fel a figyelmét ismételten arra, hogy a szelektor csak sorszámozott lehet, tehát valós típusú nem.
Gyakorlásképpen nézzünk egy újabb példát, ahol a szelektor karakteres típusú!

Példa: Olvassunk be egy karaktert, majd írjuk ki, hogy betű, számjegy, írásjel, vagy egyéb karakter-e!
program Karakter;
uses Crt;
var  Kar: char;
begin
  ClrScr;
  ReadLn(Kar);
  case Kar of
    'A'..'Z', 'a'..'z': WriteLn('betű');
    '0'..'9':           WriteLn('számjegy');
    '.', ',', '!', '?': WriteLn('írásjel');
    else                WriteLn('egyéb karakter')
  end;
  ReadLn
end.


11. Milyen típusú
A. az if utasításban a feltétel;
B. a case utasításban a szelektor?

12. Miért szintaktikai hiba az if utasításban az else előtti pontosvessző?

13. Az X mely értékénél jelenik meg a körte szó?
  If X > 5 then
    If X > 10 then
      WriteLn('alma')
  else
    WriteLn('körte');

14. Az eddig tanultak alapján hol szerepelhet end úgy, hogy nem tartozik hozzá begin?

15. Logikailag hibás az alábbi programrészlet, miért?
if x > 0 then
  WriteLn('Pozitív);
if x = 0 then
  WriteLn('Nulla')
else
  Writeln('Negatív');


13. Állhat-e a case utasítás egyik ágában egy if then else utasítás?

14. Melyik programrészlet írja ki minden esetben két szám maximumát?

A. if A >= B then Write(A);
B. if A < B then
    begin
     X := A; A := B; B := X;
    end;
   Write(A);
C. if A < B then
    begin
     A := X; X := B; B := A;
    end;
   Write(A);
D. if A < B then A := B;
     Write(A);
E. if A < B then
     Write(A)
   else
     Write(B);

15. Mi a szintaktikai hiba az alábbi programrészletben (Kar egy char típusú változó)?
case Kar of
   1: Writeln('egyes');
   2: Writeln('kettes');
   3: Writeln('hármas');
   4: Writeln('négyes');
   5: Writeln('ötös');
end;

16. Írjon programot, amely beolvas egy egész számot, majd kiírja, hogy egyjegyű-e!

17. Döntsük el a víz hőmérsékletét beolvasva, hogy milyen a halmazállapota (jég, víz, gőz). Írja meg if és case utasítással is a programot!

18. Írjon programot, amely bekéri egy dolgozatra adott pontszámot, majd kiírja az osztályzatot. (A ponthatárokat Ön határozhatja meg.)

Összefoglalás

Ön ebben a tanulási egységben megtanulta a Pascal nyelv egyszerű típusainak (egész, valós, karakteres, logikai, felsorolt és intervallum) fontosabb jellemzőit (értéktartomány, konstansok, műveletek, standard függvények és eljárások). A legtöbb irodalom a karakterlánc (string) típust is az egyszerű típusok közé sorolja, amelyet Ön a IV. tanegységben fog megtanulni. A nyelv természetesen lehetőséget ad összetett típusok használatára, definiálására is, ezzel a VI. tanegységben fogunk foglalkozni.
Az programozási feladatok megoldásához szükséges három algoritmuselem közül Ön kettőt már megtanul (szekvencia, szelekció), a harmadik (iteráció) a következő tanegységben kerül sorra.

Ajánlott irodalom

[1] Angster Erzsébet: Programozás tankönyv I, 4KÖR Bt., 1995
[2] Angster-Kertész: Turbo Pascal 6.0, Angster Erzsébet- Kertész László, 1993
[3] Angster-Kertész: Turbo Pascal ’A’..’Z’ referenciakönyv
[4] Benkő Tiborné és tsi: Programozzunk Turbo Pascal nyelven, ComputerBooks, 2001
[5] Angster-Kertész: Turbo Pascal feladatgyűjtemény, 4KÖR Bt., 1998

Gyakorló feladatok megoldása

1. 1, 2, 2, 1, 6

2. A. byte, shortint    B. integer    C. word    D. shortint    E longint

3. a:=b/3;   a jobb oldalon álló kifejezés valós, míg az a egész.
   A mod műveletnek nem lehet valós operandusa.

4. A. igaz,    B. igaz    C. hamis, a div egy operátor
D. hamis, a ReadLn egy eljárás  
E. hamis, egy azonosító számjeggyel nem kezdődhet.

5. NOT, DIV, OR, <>

6. A – 4    B – 3    C – 1    D – 2

7. B 8. A 9. D 10. B, C

11. A. logikai;
B. sorszámozott (leggyakrabban egész, vagy karakteres).

12. Azért, mert a pontosvesszővel lezárjuk az if utasítást úgy, mintha nem következne utána else ág.

13. Zavaró és emiatt némileg helyteleníthető az írásmód, hiszen az else a második if-hez tartozik, ezért a körte akkor jelenik meg, ha 5<X<=10.

14. A case utasítást lezáró end ilyen.

15. Két egymástól független if utasítást tartalmaz a programrészlet, egy egyágút és egy kétágút. X > 0 esetén a pozitív és a negatív kiírás is megjelenik. A jó megoldás az if then else if then else szerkezet.

III. tanulási egység
Iterációk

Ön e tanulási egységben megtanulja a Pascal nyelv ciklus utasításainak sajátosságait, főbb alkalmazási területeit, valamint a ciklusokra és elágazásokra épülő alapalgoritmusokat.

Ha Ön e tanulási egység tananyagát elsajátítja, képes lesz:
- a megtanult alapalgoritmusok alkalmazásával összetettebb feladatok megoldására,
- a Turbo Pascal fejlesztői környezetének tesztelésre, hibakeresésre szolgáló eszközeinek a használatára.

Tartalom
Bevezetés
1. Kezdőfeltételes ciklus
2. Végfeltételes ciklus
3. Növekményes ciklus
4. Egyszerű, ciklikus feldolgozást igénylő algoritmusok
Összefoglalás
Ajánlott irodalom
Gyakorló feladatok megoldása

Bevezetés
A strukturált programok elkészítéséhez három vezérlőszerkezetre van szükség: a szekvenciára, a szelekcióra és az iterációra. Ezen algoritmuselemek közül Ön kettőt már megtanult (szekvencia, szelekció), a harmadikkal (iteráció, vagy ciklus) ebben a tanegységben foglalkozunk. A programozás során gyakran találkozunk olyan feladattal, amikor egy tevékenységet egymás után többször kell végrehajtanunk. Egy utasítás, utasítássorozat ismételt végrehajtását nevezzük iterációnak. A programozási nyelvekben az iterációt a ciklus utasításokkal tudjuk megvalósítani. Szándékosan használtam a többes számot, hiszen a legtöbb nyelv, és így a Pascal is, több ciklus utasítást nyújt a programozók számára. Mint ahogy azt Ön az I. tanegységben már látta, előfordulhat, hogy előre ismerjük az ismétlések számát, ekkor az ún. növekményes ciklust alkalmazzuk. Ha nem ismerjük az iterációk számát, akkor egy feltételtől tesszük függővé, hogy ismételjük-e a tevékenységet. A Pascal nyelv két feltételes ciklus utasítással is rendelkezik, egy elöl- és egy hátultesztelővel.


1. Kezdőfeltételes ciklus

A kezdőfeltételes, más elnevezéssel elöltesztelő feltételes ciklus esetén a program a ciklus utasításainak végrehajtása előtt vizsgál egy feltételt, amelynek teljesülése esetén végrehajtja az utasításokat. Kérem, hogy lapozzon vissza az I. tanegység 1.2-es fejezetéhez, és tanulmányozza újra a kezdőfeltételes ciklus folyamatábráját! A ciklusnak a feltételt tartalmazó, vezérlő részét ciklusfejnek nevezzük, az ismételten végrehajtott, azaz iterált utasításokat pedig ciklusmagnak. A Pascal nyelv elöltesztelő feltételes ciklusa a while utasítás, melynek általános alakja a következő:

  while feltétel do
     utasítás

A ciklus működése tehát a következő: amíg a feltétel igaz, addig a program ismétli a ciklus magját (az utasítást), ha hamis, akkor a program a ciklust követő következő utasítással folytatódik. A feltétel egy logikai (boolean) kifejezés. Ha több utasítás szerepel a ciklus magjában, akkor Begin - End összetett utasítást (utasítás zárójelet) kell alkalmaznunk. Ha a feltétel már először sem teljesül, akkor a ciklusmag egyszer sem kerül végrehajtásra (üres ciklus), ha pedig a feltétel soha nem vesz fel hamis értéket, akkor a program végtelen ciklusba kerül. A feltétel gyakran egy reláció, például X > 0. Ekkor könnyen belátható, hogy a ciklusmagban kell lennie egy olyan utasításnak, amely megváltoztatja az X értékét. Máskülönben, ha az X kezdetben pozitív volt, akkor az is marad, azaz a program végtelen ciklusba kerül.

Nézzük egy példán keresztül a ciklus használatát!

Példa: Bankba tesszük a pénzünket kamatozni, és addig tartjuk bent, amíg milliomosok nem leszünk. A program számolja ki, hogy hány évet kell várnunk.

program Milliomos;
uses Crt;
const Kamat = 7;
var Penz: real;
    Ev: byte;
begin
  ClrScr;
  Write('A betett pénzösszeg: '); ReadLn(Penz);
  Ev := 0;
  while Penz < 1000000 do
    begin
      Penz := Penz + Penz * Kamat / 100;
      Inc(Ev)
    end;
  WriteLn(Ev, ' évnek kell eltelnie ahhoz, hogy milliomos légy!');
  ReadLn
end.

A programban használtunk egy olyan elemet, amelyet Ön a kurzus során még nem tanult meg. A program deklarációs részében, a változók deklarálása előtt egy konstanst definiáltunk:
const Kamat = 7;

A const kulcsszavat követően egy azonosítóhoz (Kamat) egy értéket rendeltünk, amely a program futása során állandó. Ezt a programozási elemet definiált konstansnak is nevezzük. Természetesen a programot e nélkül is el tudtuk volna készíteni.
Visszatérve a while ciklusra, a példából is látható, hogy előfordulhat olyan eset, amikor a ciklusmag egyszer sem hajtódik végre (a beolvasott pénzösszeg eléri az egymillió forintot.) Ez az elöltesztelő ciklusok sajátossága. Szintén látható az, amire fentebb utaltunk, hogy a ciklusmag megváltoztatja (növeli) a Penz változó értékét, amely a ciklusfej feltételében szerepel.
A példa kapcsán beszélhetünk a változók kezdő értékadásáról, más szóval inicializálásáról. Mivel a program indulásakor a változók értéke határozatlan, ezért azokat a változókat inicializálnunk kell, amelyeknek lényeges, hogy mi a kezdőértékük. Példánkban ilyen az Ev változó. Rossz szokás viszont a program elején minden változót „kinullázni”, hiszen pl. a Penz változónak lényegtelen a kezdőértéke, mivel azt a program beolvassa a billentyűzetről.

2. Végfeltételes ciklus

Ebben a fejezetben a Pascal nyelv hátultesztelő feltételes, más elnevezéssel végfeltételes ciklus utasításáról, a repeat - until utasításról lesz szó. Itt az iterációt vezérlő feltételt a program a ciklusmag végrehajtása után értékeli ki, amelyből az következik, hogy a ciklusmag legalább egyszer végrehajtódik. Ez az egyetlen lényegi különbség a kezdőfeltételes ciklushoz képest. Kérem, hogy lapozzon vissza az I. tanegység 1.2-es fejezetéhez, és tanulmányozza újra a végfeltételes ciklus folyamatábráját! A while és a repeat ciklust hasonló típusú feladatok megoldására használhatjuk (azaz olyankor, amikor nem tudjuk előre az ismétlések számát), esetleg az egyik egy kicsit kényelmesebb megoldást nyújt. A repeat- until ciklus általános alakja:

  repeat
    utasítás(ok)
  until feltétel;

Az utasítás(ok) végrehajtását meg kell ismételni, ha a feltétel hamis. Ha a feltétel igaz, a program a ciklus utáni utasítással folytatódik. A ciklusmagot a repeat - until kulcsszavak fogják közre, nem szükséges Begin - End utasítás zárójelet használnunk.

Oldjuk meg a Milliomos programot a repeat ciklus haszná-latával!

Példa
program Milliomos2;
uses Crt;
const Kamat = 7;
var Penz: real;
    Ev: byte;
begin
  ClrScr;
  Write('A betett pénzösszeg: '); ReadLn(Penz);
  Ev := 0;
  repeat
    Penz := Penz + Penz * Kamat / 100;
    Inc(Ev)
  until Penz >= 1000000;
  WriteLn(Ev, ' évnek kell eltelnie ahhoz, hogy
                milliomos légy!');
  ReadLn
end.

Vigyázzunk, hogy a feltétel pont az ellentéte a while ciklusénak! Mivel a ciklusmag egyszer mindenképpen lefut, ha a betett összeg egymillió forintnál nagyobb, akkor is 1 év megoldást kapunk. Ezt az apró hibát egy elágazással kiküszöbölhetjük:

program Milliomos2;
uses Crt;
const Kamat = 7;
var Penz: real;
    Ev: byte;
begin
  ClrScr;
  Write('A betett pénzösszeg: '); ReadLn(Penz);
  if Penz >= 1000000 then
    WriteLn('Már milliomos vagy')
  else
    begin
      Ev := 0;
      repeat
        Penz := Penz + Penz * Kamat / 100;
        Inc(Ev)
      until Penz >= 1000000;
      WriteLn(Ev, ' évnek kell eltelnie ahhoz, hogy
              milliomos légy!');
    end;
  ReadLn
end.

A megoldásban jól látható, hogy az egymásba ágyazott utasításokat a szinteknek megfelelően mindig egy kicsit beljebb kezdtük. Ez a program áttekinthetőségét, olvashatóságát segíti elő.

Ha tudjuk, hogy a ciklusmag legalább egyszer végrehajtódik, akkor a két eddig tanult ciklus közül célszerű a repeat ciklust használni. Egy tipikus példa az ellenőrzött adatbevitel.

Példa: Az elágazásoknál látott Honap programot egészítsük ki úgy, hogy csak 1 és 12 közötti számot fogadjon el.

program Honap3;
uses Crt;
var Ho: byte;
begin
  ClrScr;
  repeat
    Write('Hónap: ');
    ReadLn(Ho);
  until (Ho >= 1) and (Ho <= 12);
  case Ho of
    1, 2, 12: WriteLn('Tél');
    3..5:     WriteLn('Tavasz');
    6..8:     WriteLn('Nyár');
    9..11:    WriteLn('Ősz');
  end;
  ReadLn
end.

3. Növekményes ciklus

A növekményes ciklust (hívják még számláló ciklusnak is), akkor használjuk, ha előre meghatározott a ciklusmag végrehajtásainak a száma. Ezt a ciklust szinte minden  programozási nyelvben a for szócska jelöli. A Pascalban a for ciklus szintaktikája:

for ciklusváltozó := kezdőérték to végérték do
    utasítás;

vagy
for ciklusváltozó:=kezdőérték downto végérték do
    utasítás;

ahol a ciklusváltozó sorszámozott típusú változó, a kezdőérték és a végérték pedig sorszámozott típusú kifejezések, mind a három azonos vagy kompatíbilis típusú. A ciklusváltozó a kezdőértéktől a végértékig egyesével nő (to) vagy csökken (downto). Az utasítás (vagy a begin - end utasítás zárójelek közé zárt utasításcsoport) a ciklusváltozó minden értékénél végrehajtódik. Elöltesztelő ciklus, így ha a kezdőérték > végérték (to esetén), vagy a kezdőérték < végérték (downto esetén), akkor a ciklusmag egyszer sem kerül végrehajtásra (üres ciklus).

Nézzünk példákat a ciklus használatára!

Példa: Írjunk ki N db csillagot a képernyőre! N értékét a billentyűzetről olvassuk be!
program Csillag1;
uses Crt;
var n, i: byte;
begin
  ClrScr;
  Write('Mennyi csillag legyen? '); ReadLn(N);
  for i := 1 to N do
    Write('*');
  ReadLn
end.

A ciklusváltozó gyakran az i azonosítót kapja.

Példa: Adjuk össze 1-től 100-ig az egész számokat!
program Osszegzes;
uses Crt;
var Osszeg: word;
    i: byte;
begin
  ClrScr;
  Osszeg := 0;
  for i := 1 to 100 do
    Osszeg := Osszeg + i;
  WriteLn('Az összeg: ', Osszeg);
  ReadLn
end.

A ciklusváltozó értékét nem szabad a ciklusban megváltoztatni (bár meg lehet), viszont fel lehet használni, mint ahogy ebben a példában is tettük.

Példa: Írjuk ki az angol nagybetűs ABC-t visszafelé!
program ABC;
uses Crt;
var Betu: char;
begin
  ClrScr;
  for Betu := 'Z' downto 'A' do
    Write(Betu:2);
  ReadLn
end.

Itt a ciklusváltozó, valamint a kezdő és végérték karakteres típusú. A downto használata miatt a ciklusváltozó értéke mindig eggyel csökken, azaz az előző betű lesz.

Példa: Írjunk ki 10 sor csillagot a képernyőre úgy, hogy az első sorban 1, a másodikban 2, az i. sorban pedig i db csillag legyen!
program Csillag2;
uses Crt;
var i,j: byte;
begin
  ClrScr;
  for i := 1 to 10 do
    begin
      for j := 1 to i do Write('*');
      WriteLn;
    end;
  ReadLn
end.

A példában az i ciklusváltozójú (külső) ciklusba ágyaztunk egy újabb for ciklust. A külső a ciklus magjában szerepel még egy soremelés is. Természetesen az egymásba ágyazott ciklusoknak nem lehet azonos a ciklusváltozójuk.

Ahogy haladunk előre a kurzusunkban, algoritmusaink, programjaink egyre bonyolultabbakká válnak, így a hibalehetőség is egyre több. Ön már az I. tanegységben megtanulta, és az eddigi programozói munkája során tapasztalta, hogy a szintaktikai hibákat a fordító felismeri és jelzi. Felmerülhet a kérdés, hogy milyen lehetőségeink vannak a logikai, szemantikai hibák felderítésére, kijavítására. A Turbo Pascal fejlesztői környezete, hasonlóan más rendszerekhez, biztosít eszközöket a futó program tesztelésére. Nézzük ezek közül a fontosabbakat!
Programunkat futtathatjuk lépésenként, az F7 (vagy Run / Trace into menüpont) leütésére egy csak programsort hajt végre a rendszer. A lépésenkénti végrehajtást célszerű összekapcsolni a változók (vagy akár kifejezések) értékeinek a figyelésével. Ezt a Debug / Add watch... menüponttal tudjuk elérni, amellyel egy figyelő (Watch) ablakba felvehetünk változókat, kifejezéseket. Ha a Window / Tile menüpontot kiválasztjuk, akkor a képernyőn egyidejűleg láthatjuk a programunk forráskódját, amelyet futtathatunk lépésenként, valamint a Watch ablakot.
Előfordulhat, hogy egy hosszabb programot csak egy bizonyos ponttól akarunk lépésenként futtatni. Ez megoldható egy ún. töréspont elhelyezésével (Debug / Add breakpoint), ahol a program végrehajtása várakozik, és visszakerülünk a fejlesztői környezetbe. Ezután folytathatjuk lépésenkénti futtatással is a programunkat.
Futtathatjuk a programunkat a kurzor aktuális pozíciójáig is (Run / Go to cursor).
A fenti módszerek valamelyikével ideiglenesen megállított programunkat véglegesen befejezhetjük a Run / Program reset menüponttal


1. Igaz, vagy hamis?
A. A While utasítás egy kezdőfeltételes ciklus.
B. A Repeat ciklus magja legalább egyszer végrehajtódik.
C. A While ciklusmagját meg kell ismételni, ha a feltétel hamis.
D. Előfordulhat, hogy a For ciklus magja egyszer sem fut le.
E. A For ciklus ciklusváltozója csak sorszámozott típusú lehet.

2. Hány A betűt írunk ki:
  for i := 5 to 10 do
    Write('A');

3. Mi a hiba:
for i := 1 to Sqrt(N) do
  Write(i);

4. Mely ciklusok futnak le pontosan egyszer I=20 esetén?
A.  While I<20 Do Inc(I);
B.  While I>=20 Do Dec(I);
C.  Repeat
     Inc(I)
    Until I>0;
D.  For J:=2 To 3 Do Inc(I);

5. Adott a következő programrészlet:
Program MitIrokKi;
Var I :Byte;
Begin
  I:=1;
  While I<10 Do
    Begin
      If (I Mod 2)=0
        Then Write(I:2);
      Inc(I);
    End;
End.

Mi jelenik meg a program futása után a képernyőn?
A.  1 2 3 4
B.  3 5 7 9
C.  2 4 6 8
D.  0 2 4 6

6. Mely ciklus(ok) az(ok), amely(ek) U ciklusmagja A=12 esetén egyszer sem fog végrehajtódni?
A. For I := 1 To A Do U;
B. For I := 15 To A Do U;
C. Repeat U Until A = 15;
D. While A >= 15 Do U;


1. Hány A betűt írunk ki:
  for i := 1 to 10 do
    for j := 1 to 10 do
      Write('A');

2. Mit ír ki az alábbi programrészlet:
For I:=1 To 5 Do
  If I MOD 2 = 1 Then
    Write('-')
  Else
    Write('+');

A. -+-+-
B. ---++
C. +-+-+
D. +++++

3. Az I változó kezdőértéke 10. Mely ciklusmag hajtódik végre legalább egyszer?
A. While I<10 Do
     Inc(I);

B. While I>=10 Do
     Dec(I);

C. Repeat
     Dec(I)
   Until I<20;

D. For I:=11 To 20 Do
     Inc(J);

4. Az I Byte típusú változó. Mi lesz I értéke az alábbi ciklusból való kilépés után:
I:=5;
While (I Mod 3) <> 2 Do
  Inc(I);

A. I=11
B. I=5
C. I=8
D. Semmi, a program kilépés nélküli végtelen ciklusba esik

5. Készítsen programot a for ciklus felhasználásával, amely kiírja a 100-nál kisebb 3-mal osztható pozitív számokat!

6. Készítsen programot a while ciklus felhasználásával, amely kiírja a 100-nál kisebb 3-mal osztható pozitív számokat!

7. Készítsen programot a repeat ciklus felhasználásával, amely kiírja a 100-nál kisebb 3-mal osztható pozitív számokat!

8. Írja meg úgy a nagybetűt kisbetűvé alakító programot, hogy beolvasáskor a program csak nagybetűt fogadjon el!




4. Egyszerű, ciklikus feldolgozást igénylő algo-ritmusok

Ön már megtanulta a Pascal nyelv fontosabb utasításait, amelyekkel bármilyen algoritmust kódolni tud. Az utasításokat két csoportba sorolhatjuk. Az egyszerű utasítások közé tartoznak az értékadó és az eljáráshívó utasítás, míg a strukturáltak közé az összetett utasítás (begin - end), a szelekciós utasítások (if, case) és az iterációk (for, while, repeat). Ebben a fejezetbe Ön megtanulja néhány gyakran használt alapalgoritmusnak az általános sémáját, valamint alkalmazza azokat.

Adatok feldolgozása végjelig
Nem ismerjük az iterációk számát, ciklus végrehajtása attól függ, hogy van-e még feldolgozandó adat. Az adatfolyam végét egy végjel jelzi. A megoldás általános algoritmusa:

Be: Adat
Ciklus amíg Adat <> Végjel
  Az adat feldolgozása
  Be: Adat
Ciklus vége

Mivel a végjelet nem kell feldolgoznunk, ezért célszerű az adat beolvasását a ciklusmag végén elvégeznünk, így a vezérlő feltétel következő kiértékelésekor befejeződik a ciklus. Ez első adatot a ciklus előtt olvassuk be, ezt előolvasásnak nevezzük. Mivel kezdőfeltételes ciklussal oldottuk meg a problémát, az algoritmus akkor is jól működik, ha nincs adat, azaz már elsőre a végjelet olvassa be a program.

Példa: Olvassuk be körök sugarait nulla végjelig (addig, amíg nullát nem ütünk), majd írjuk ki a kerületét és a területét!
program Korok;
uses Crt;
var R, K, T: real;
begin
  ClrScr;
  Write('Sugár: '); ReadLn(R);
  while R <> 0 do
    begin
      K := 2 * R * Pi;
      T := R * R * Pi;
      WriteLn('A kerülete:', K:8:2);
      WriteLn('A területe:', T:8:2);
      WriteLn;
      Write('Sugár: '); ReadLn(R);
    end;
end.

Megszámlálás
Egy sorozat, adatfolyam valamilyen adott tulajdonságú elemeinek a számát akarjuk meghatározni. Felveszünk egy számláló változót, melynek az értékét növeljük, ha az elem kívánt tulajdonságú. A számlálót természetesen le kell nulláznunk. Ha előre ismerjük az elemek számát, akkor for ciklust használhatunk, ha nem akkor az előző pontban megismert végjelig történő feldolgozást alkalmazzuk. Nézzük a két eset általános leírását, majd példákat!

Számláló := 0
Ciklus I := 1-től N ig       {N az adatok száma }
  Hozzáférés az I. elemhez    {pl. beolvasása}
  Ha az I. elem az adott tulajdonságú akkor
    Számláló := Számláló + 1
  Elágazás vége
Ciklus vége


Számláló := 0
Hozzáférés az első elemhez
Ciklus amíg az Elem <> Végjel
  Ha az Elem az adott tulajdonságú akkor
    Számláló := Számláló + 1
  Elágazás vége
  Hozzáférés a következő elemhez
Ciklus vége

Példa:
1. Olvassunk be 10 számot, és írjuk ki, hogy mennyi hárommal osztható volt közöttük!
program Oszthato1;
uses Crt;
var A, Szamlalo, i: integer;
begin
  ClrScr;
  Szamlalo := 0;
  for i := 1 to 10 do
    begin
      Write('Kérem az ', i, '. számot: ');
      ReadLn(A);
      if A mod 3 = 0 then
        Inc(Szamlalo);
    end;
  Writeln(Szamlalo, ' hárommal osztható volt.');
  ReadLn
end.


2. Olvassunk be számokat nulla végjelig, és írjuk ki, hogy mennyi hárommal osztható volt közöttük!
program Oszthato2;
uses Crt;
var A, Szamlalo: integer;
begin
  ClrScr;
  Szamlalo := 0;
  Write('Kérem a számot: '); ReadLn(A);
  while A <> 0 do
    begin
      if A mod 3 = 0 then
        Inc(Szamlalo);
      Write('Kérem a számot: '); ReadLn(A);
    end;
  Writeln(Szamlalo, ' hárommal osztható volt.');
  ReadLn
end.


Összegzés
Egy sorozat elemeit valamilyen módon gyűjteni kell, összegezni, esetleg a szorzatukat képezni.
Ismert elemszám esetén az algoritmus:

Összeg := 0
Ciklus I := 1-től N ig
  Hozzáférés az I. elemhez    {pl. beolvasása}
  Összeg := Összeg + I. elem
Ciklus vége


Ismeretlen elemszám esetén az algoritmus:
Összeg := 0
Hozzáférés az első elemhez
Ciklus amíg az Elem <> Végjel
  Összeg := Összeg + I. elem
 Hozzáférés a következő elemhez
Ciklus vége

Ön már találkozott ismert számú adat összegzésével, a 3. fejezet egyik példája ilyen. Kérem lapozzon vissza. Ott a Hozzáférés az I. elemhez művelet kimaradt, hiszen az adat maga I volt.

Példa: 0 végjelig olvassunk be számokat, és számoljuk ki a párosak összegét! Itt egy picit bonyolítottuk az algoritmus, hiszen nem minden adatot kell összegeznünk.
program Oszegzes;
uses Crt;
var Osszeg, A: integer;
begin
  ClrScr;
  Osszeg := 0;
  Write('Kérem a számot: '); ReadLn(A);
  while A <> 0 do
    begin
      if A mod 2 = 0 then
        Osszeg := Osszeg + A;
      Write('Kérem a számot: '); ReadLn(A);
    end;
  Writeln('A párosak összege: ', Osszeg);
  ReadLn
end.


Átlagszámítás
Az átlagszámítás során egy megszámlálást és egy összegzést kell párhuzamosan végeznünk.

Példa: 0 végjelig olvassunk be számokat, és számoljuk ki az átlagukat!
program Atlag_Szamitas;
uses Crt;
var Osszeg, A, Szamlalo: integer;
    Atlag: real;
begin
  ClrScr;
  Osszeg := 0;
  Szamlalo := 0;
  Write('Kérem a számot: '); ReadLn(A);
  while A <> 0 do
    begin
      Osszeg := Osszeg + A;
      Inc(Szamlalo);
      Write('Kérem a számot: '); ReadLn(A);
    end;
  Atlag := Osszeg / Szamlalo;
  Writeln('A számok átlaga: ', Atlag:8:2);
  ReadLn
end.


Minimum és maximum kiválasztás
Egy sorozat legkisebb, illetve legnagyobb elemét kell kiválasztanunk. Az algoritmus lényege, hogy használunk egy Min, illetve Max változót, amely a már beolvasott elemek minimumát, illetve maximumát tartalmazza. Ha az éppen vizsgált adat kisebb, mint a minimum, akkor ez lesz a minimum új értéke. Hasonlóan járunk el a maximumnál. A Min és Max kezdőértékének az első adatot választjuk.

Ismert elemszám esetén az algoritmus:
Hozzáférés az első elemhez
Min := 1. elem
Max := 1. elem
Ciklus I := 2-től N ig
  Hozzáférés az I. elemhez    {pl. beolvasása}
  Ha az I. elem < Min akkor
    Min := I. elem
  Egyébként
    Ha az I. elem > Max akkor
      Max := I. elem
    Elágazás vége
  Elágazás vége
Ciklus vége

Ismeretlen elemszám esetén az algoritmus:
Hozzáférés az első elemhez
Min := 1. elem
Max := 1. elem
Ciklus amíg az Elem <> Végjel
  Ha Elem < Min akkor
    Min := Elem
  Egyébként
    Ha Elem > Max akkor
      Max := Elem
    Elágazás vége
  Elágazás vége
  Hozzáférés a következő elemhez
Ciklus vége


Példa: 0 végjelig olvassunk be számokat, és írjuk ki a legkisebbet és a legnagyobbat!
program MinMax;
uses Crt;
var A, Min, Max: integer;
begin
  ClrScr;
  Write('Kérem a számot: '); ReadLn(A);
  Min := A;
  Max := A;
  while A <> 0 do
    begin
      if A < Min then
        Min := A
      else
        if A > Max then
          Max := A;
      Write('Kérem a számot: '); ReadLn(A);
    end;
  WriteLn('A legkisebb szám: ', Min);
  WriteLn('A legnagyobb szám: ', Max);
  ReadLn
end.


Menükészítés

Azt szeretnénk elérni, hogy programunkat a felhasználó egy egyszerű menüvel tudja vezérelni. Fel kell kínálnunk a választási lehetőségeket, majd a kiválasztott menüpont végeztével újra meg kell jelenítenünk a menüt. Tehát egy ciklust kell alkalmaznunk, mégpedig célszerűen egy hátultesztelőt, hiszen a menüt legalább egyszer meg kell jelenítenünk. Az általános algoritmus:

  Ciklus
    A felkínált lehetőségek megjelenítése
    Választás bekérése
    Válogatás az esetek között a Választás
     alapján
  Mígnem a Választás = Vége
  Ciklus vége

Példa: A program a következő menüpontokat tartalmazza: 1. Írja ki a bekért karakter ASCII kódját;  2. Írja ki a bekért kódhoz tartozó karaktert;  3. (Esc) Fejezze be a programot!
program Menu;
uses Crt;
var Valasz, Kar: char;
    Kod: byte;
begin
repeat
  ClrScr;
  WriteLn('1. Karakter -> ASCII kód');
  WriteLn('2. ASCII kód -> karakter');
  WriteLn('3. (ESC) Vége');
  Valasz := ReadKey;
  case Valasz of
    '1': begin
           ClrScr;
           Write('Kérek egy karaktert: ');
           ReadLn(Kar);
           WriteLn('Az ASCII kódja: ',Ord(Kar));
           ReadLn;
         end;
    '2': begin
           ClrScr;
           Write('Kérek egy kódot: ');
           ReadLn(Kod);
           WriteLn('A neki megfelelő karakter: ',
                    Chr(Kod));
           ReadLn;
         end;
  end;
until (Valasz = '3') or (Valasz = #27);
end.

Néhány megjegyzés a példával kapcsolatban. A menüpontok szerinti elágazást célszerű egy case utasítással elvégezni. A választást a Crt unitban található ReadKey függvénnyel érzékeli a program (ld. IV tanegység), amely függvény a leütött karaktert adja vissza. Ez jobb a program vezérlésére, mint a ReadLn eljárás, hiszen ott még egy Entert is kellene ütnünk. Az is előny a ReadKey mellett, hogy a leütött billentyűnek megfelelő karakter nem jelenik meg a képernyőn. A programból kiléphetünk akár a 3, akár az Esc leütésével, mely utóbbi karakterre a programban az ASCII kódjával tudunk hivatkozni (#27- a 27-es kódú karaktert jelent, ld. Char típusú konstansok). Vigyázzunk, hogy az egyes menüpontok végén iktassunk be egy várakozást, mert máskülönben rögtön megjelenítené programunk a menüt és így letörölné a képernyőt.

7. A végjelig történő feldolgozásnál a ciklusmagban a két tevékenység miért a következő sorrendben követi egymást?
  Az adat feldolgozása
  Be: Adat

8. Milyen két műveletből áll az átlagszámítás?

9. Menü készítésekor melyik ciklus alkalmazása célszerű, és miért?

10. A minimum és maximum kiválasztás algoritmusában melyik megoldás nyújt hatékonyabb kódot: a fenti példában szereplő két egymásba ágyazott elágazás, vagy az alábbi két egymástól független elágazás?
  if A < Min then
    Min := A;
  if A > Max then
    Max := A;


9. Olvassunk be számokat 0 végjelig, és a kétjegyűeknek számoljuk ki az átlagát!

10. Olvasson be karaktereket ’*’ végjelig. Írja ki a bevitt karakterek számát! Számolja meg, hány szóköz és hány kérdőjel volt közöttük! Írja ki, melyik volt a legkisebb, illetve melyik a legnagyobb ASCII kódú karakter!

11. Írjon menüvel vezérelt programot, amely síkidomok (háromszög, téglalap, kör) területét számolja ki!

Összefoglalás

Ön ebben a tanegységben megtanulta a Pascal nyelv iterációs utasításait: a while elöltesztelő feltételes ciklust, a Repeat – until hátultesztelő feltételes ciklust és a for növekményes ciklus. Az, hogy a nyelv három iterációs utasítással rendelkezik a programozó kényelmét szolgálja. Ön programozási feladatokon keresztül megtanulta az ciklusok tipikus alkalmazási területeit és a legfontosabb alapalgoritmusokat: adatok feldolgozása végjelig, megszámlálás, összegzés, átlagszámítás, menükészítés, minimum- és maximum kiválasztás.

Ajánlott irodalom

[1] Angster Erzsébet: Programozás tankönyv I, 4KÖR Bt., 1995
[2] Angster-Kertész: Turbo Pascal 6.0, Angster Erzsébet- Kertész László, 1993
[3] Angster-Kertész: Turbo Pascal ’A’..’Z’ referenciakönyv
[4] Benkő Tiborné és tsi: Programozzunk Turbo Pascal nyelven, ComputerBooks, 2001
[5] Angster-Kertész: Turbo Pascal feladatgyűjtemény, 4KÖR Bt., 1998




Gyakorló feladatok megoldása

1. A. igaz,    B. igaz,    C. hamis,    D. igaz,    E. igaz

2. 6 A betűt, mivel a kezdő és a végértékre is végrehajtódik a ciklus.

3. A végérték, Sqrt(N), nem sorszámozott típusú (valós).

4. B, C

5. C

6. B, D

7. Mert így az utolsónak beolvasott adatot, azaz a végjelet már nem dolgozzuk fel.

8. Összegzés, megszámlálás

9. A repeat ciklusé, hiszen a menüt legalább egyszer megjelenítjük.

10. Az egymásba ágyazás, hiszen akkor A < Min esetben a második feltételt már nem értékeli ki a program.

IV. tanulási egység
Karakterlánc kezelés, karakteres és grafikus képernyő kezelése

Ön e tanulási egységben elsajátítja a szöveges adatok kezeléséhez nélkülözhetetlen karakterlánc (string) adattípus használatát, valamint kurzusunk fő vonalától kissé kitérve belekóstol a CRT és a Graph unit fontosabb elemeinek alkalmazásába. Mindeközben algoritmuskészsége tovább fejlődik.
Ha Ön e tanulási egység tananyagát elsajátítja, képes lesz:
- szöveges adatokkal dolgozni,
- a referenciakönyv, vagy az IDE help segítségével a CRT és a Graph unit lehetőségeit használni.

Tartalom
Bevezetés
1. A karakterlánc (string) típus
2. A karakteres képernyő kezelése – a CRT unit
3. A Turbo Pascal grafikája – a Graph unit
Összefoglalás
Ajánlott irodalom
Gyakorló feladatok megoldása

Bevezetés

A II. tanulási egység összefoglalásában már említettük, hogy legtöbb irodalom a karakterlánc (string) típust is az egyszerű típusok közé sorolja, annak ellenére, hogy egy string további részekre, karakterek osztható. Ennek egyik oka az lehet, hogy a ReadLn eljárással egy karakterláncot be tudunk olvasni, valamint a Write(Ln) eljárással ki tudunk íratni. Ezen nagyon fontos, gyakran alkalmazott típussal teljessé válik az Ön által megtanult egyszerű típusok köre.
A tanegység második részében Ön belekóstol a CRT és a Graph unit néhány eszközének az alkalmazásába. Itt nem követelünk meg Öntől felesleges memorizálást, a Pascal fejlesztői környezetének a Helpjét, valamint a referenciakönyveket kell tudni használnia.

1. A karakterlánc (string) típus
A II. tanegységben már említettük, hogy a szöveges adatok feldolgozására a Pascal nyelv két egyszerű adattípus nyújt: a karakteres (char) és a karakterlánc (string) típust.
Egy string típusú változót két féleképpen deklarálhatunk, például:

  var S1: string;
      S2: string[20];

A változóknak értéket adhatunk beolvasással, valamint értékadó utasítással, például:

  S1 := 'Üdvözöllek!';
  ReadLn(S2);

Menjünk végig a megszokott módon a string típus jellemzőin!

a. Értéktartomány
Egy string típusú változóban tárolt karakterlánc hossza a program futása során változhat, de maximálva van. A példánkban az S1 deklarálásakor a string kulcsszó után nem írtunk semmit, ekkor a rendszer 256 bájtot foglal le a memóriában az S1 számára. Egy bájt szükséges a karakterlánc aktuális hosszának a nyilvántartására, így az S1 hossza 0 és 255 karakter között változhat. A másik fajta deklarálás során megadhatunk egy 256-nál kisebb maximális hosszt, a példánkban S2 hossza 0 és 20 karakter között változhat.

b. Konstansai
Ön már gyakran használt karakterlánc konstansokat a Write(Ln) eljárásokban. Pl. Write('Kérem az első számot'). Létezik üres, nulla hosszúságú karakterlánc konstans, ami két aposztróf közvetlenül egymás után, például:

  S1 := '';

c. Műveletek
Ön már megtanulta, hogy műveletek segítségével kifejezéseket alkothatunk, programjaiban használt már numerikus, karakteres, és logikai kifejezéseket. A karakterlánc kifejezések alkotását egy művelet, az összefűzés, idegen eredetű szóval konkatenálás segíti. Ennek a jele a  +. Például:

  V_Nev := 'Kovács';
  K_Nev := 'István';
  Nev := V_Nev + K_Nev;

Karakterlánc típusú operandusokra elvégezhetők a relációs műveletek, melyek természetesen logikai értéket eredményeznek. Nagyon kényelmes jellemzője a Pascal nyelvnek, hogy két karakterlánc relációját az első különböző karakter ASCII kódja alapján állapítja meg. Nézzünk példákat!

  'Egér' > 'Billentyűzet'  {igaz}
  'Egér' < 'Erdő'          {igaz}
  'Egér' = 'egér'          {hamis}
  'Éger' > 'Tölgy'         {igaz!}

A példák is mutatják, hogy vigyáznunk kell a kis- és nagybetűkkel, valamint a magyar ékezetes karakterekkel.

A karakterláncoknak hozzáférhetünk az egyes karaktereihez az indexelés segítségével, például S1[5] az S1 karakterlánc 5. karakterét jelenti. Az így kapott objektum egyenértékű egy karakteres változóval.

Példa:
  Nev := 'kovács';
  Nev[1] := Upcase(Nev[1]);

Hasonlóan, mint az egész és valós számok esetén a karakterlánc és a karakteres típus az egyik irányban értékadás kompatíbilis:

  var Kar: char;
      S: string;
  ...
  S := Kar;     {jó}
  Kar := S;     {szintaktikai hiba}

d. Szabványos eljárások, függvények
A karakterláncok kezelését nagymértékben megkönnyítik a Turbo Pascal standard függvényei eljárásai. Önnek ismernie kell ezen eszközöket, de nem követelmény, hogy pontos szintaktikájukat „fejből” tudja, mivel ezek az alprogramok kissé bonyolultabb paraméterezésűek, mint az eddigiek. Ilyen esetekben a programozó használhatja, sőt kell hogy használja a fejlesztői környezet Helpjét, vagy egy referenciakönyvet. A Turbo Pascal rendszerben az F1 billentyűvel jelenítheti meg a Segítséget. Egy gyors lehetőség a Ctrl-F1 billentyűkombináció, mely arról a programelemről (pl. függvényről) jelenít meg Help leírást, amelyen a kurzor áll. A referenciakönyvek abban különböznek a tankönyvektől, hogy könnyen kikereshető módon (pl.ABC sorrendben) tartalmazzák a nyelv elemeit. Ilyenek a [2, 3] irodalmak.
A string kezelő alprogramok szintaktikai leírását olyan formában közöljük, ahogy az a Helpben és a referenciakönyvekben is található. Nézzük sorra Őket:


Függvények:

Length
Egy karakterlánc hosszával tér vissza.
Deklarációja:

function Length(S: String): Integer;

Nézzük, hogyan kell értelmezni ezt a szintaktikai leírást. A function kulcsszó jelzi, hogy függvényről van szó. (Természetesen ezt a függvény használatakor, hívásakor nem kell a programba beírnunk.) A függvénynek egy paramétere van, ami string típusú. A paraméter(ek) után kettősponttal elválasztva következik a függvény visszatérési értékének a típusa, ami a Length esetén integer. (Az V. tanegységben Ön is fog deklarálni saját függvényeket, eljárásokat. A Helpben, a referenciakönyvekben a deklarációk fejlécét adják meg.)

Példa:
var
  S: String;
begin
  Readln (S);
  Writeln('"', S, '"');
  Writeln('hossz = ', Length(S));
end.

Pos
Egy rész karakterláncot keres egy karakterláncban.
Deklarációja:

function Pos(Substr: String; S: String): Byte;

Megjegyzés:
Egy egész számmal tér vissza, amely a Substr rész karakterlánc első előfordulásának a pozíciója az S karakterláncban. Ha S nem tartalmazza, akkor 0-val tér vissza. Gyakran csak arra használjuk, hogy egy rész karakterlánc szerepel-e egy stringben.

Példa:
var S: String;
begin
  S := '   123.5';
  { A szóközöket 0 karakterekké konvertálja }
  while Pos(' ', S) > 0 do
    S[Pos(' ', S)] := '0';
end.

Ez a kissé bonyolult, de hasznos példa szerepel a Turbo Pascal Helpjében. A program a karakterláncban előforduló szóközöket lecseréli '0' karakterekre. A while ciklus akkor áll le, ha Pos(' ', S) > 0 feltétel hamis lesz, azaz már nincs több szóköz az S-ben.

Copy
A karakterlánc egy részével tér vissza.
Deklarációja:
function Copy(S: String; Index: Integer; Count:
              Integer): String;

Megjegyzés:
A Copy egy olyan karakterlánccal tér vissza, amely az S-ből Count db karaktert tartalmaz és az Index-edik pozícióban kezdődik.

Példa:
var S: String;
begin
  S := 'ABCDEF';
  S := Copy(S, 2, 3); { 'BCD' }
end.

Concat
Stringek sorozatát egymáshoz fűzi, konkatenálja. (Kiváltható az egyszerűbb + művelettel.)
Deklarációja:

function Concat(s1 [, s2,..., sn]: String): String;

Példa:
var
  S: String;
begin
  S := Concat('ABC', 'DEF'); { 'ABCDEF' }
end.


Eljárások:

Str
Egy numerikus értéket karakterlánccá konvertál.
Deklarációja:

Procedure Str(X [: Mezőszélesség [: Tizedesjegyek ]];
               var S:string);

Megjegyzés:
Az X numerikus kifejezést egy olyan stringgé konvertálja, amely a kifejezés kiírásakor megjelenne, és elhelyezi az S változóban.

Példa:
uses graph;
var eredm: real;
      s:string;
begin
  ...
  ...
  Str(eredm:10:2, s);
  OutTextXY(10, 10, s);   {Grafikus módban csak stringet írathatunk ki a képernyőre}
end.

A példa előremutat a tanegységben később sorra kerülő Graph unit egyik eszközére, a kiíró OutTextXY eljárásra. Ezen eljárás (ahogy a neve is mutatja) csak szöveget képes kiírni a grafikus képernyőre. Tehát pl. 123-at nem (ez egy számkonstans), de '123'-at  (ez egy szövegkonstans) ki tudja írni. Az 123   '123' konverziót végzi el az Str eljárás.
Önnek is feltűnhetett, hogy a szintaktikai leírásban az S paraméter előtt szerepel a var szócska. Egyelőre annyit tanuljon meg ezzel kapcsolatban, hogy ilyen esetben az eljárás megváltoztatja a paraméter értékét (az S-ben elhelyezi a konvertálás eredményét). Az eljárás hívásakor ezen paraméter helyére csak változót írhatunk. A többi (var nélküli) paraméter helyén híváskor kifejezés állhat.

Val
Egy karakterláncot numerikus értéké konvertál. Az Str fordítottjának tekinthető.
Deklarációja:

Procedure Val(S; var V; var Kod: Integer);

Megjegyzés:
S egy karakterlánc, amely egy előjeles számot alkot, V egy integer vagy egy real típusú változó, Kod egy integer változó. Az S karakterláncot V típusának megfelelően numerikussá konvertálja, és elhelyezi a V változóban. Ha sikerült az átalakítás, akkor a Kod értéke 0, egyébként az átalakítás során talált első hibás karakter pozíciója (pl az '12k3' karakterláncot nem lehet számmá konvertálni, a hibakód 3 lesz).

Példa:
var A, Kod: Integer;
begin
  Val('123?', A, Kod);
  if Kod = 0 then
    WriteLn(A)
  else
    WriteLn('Hiba a ', Kod,'. pozícióban)
end.

A következő példában a Val eljárást ellenőrzött adatbevitelre használjuk. (A program nem áll le futási hibával, ha nem számkonstans formátumú adatot írunk be a billentyűzetről.)

Példa:
program Beolvas;
uses Crt;
var Szam, Kod: integer;
    S: string;
begin
  ClrScr;
  repeat
    Write('Kérek egy számot: '); ReadLn(S);
    Val(S, Szam, Kod);
    if Kod <> 0 then WriteLn('Hiba!!!');
  until Kod = 0;
  WriteLn('Jó! ', Szam);
  ReadLn
end.

Delete
A karakterlánc egy részét kitörli.
Deklarációja:

procedure Delete(var S: String; Index: Integer;
                  Count:Integer);

Megjegyzés:
A Delete eljárás kitöröl Count db karaktert az S sztringből az Index-edik karaktertől kezdődően.

Példa:
var
  s: string;
begin
  s := 'Honest Abe Lincoln';
  Delete(s,8,4);
  Writeln(s); { 'Honest Lincoln' }
end.

Insert
Egy karakterláncot beilleszt egy másikba.
Deklarációja:

procedure Insert(Source: String; var S: String;
                  Index: Integer);

Megjegyzés:
Az Insert eljárás beilleszti a Source stringet az S-be az Index-edik karaktertől kezdődően.

Példa:
var
  S: String;
begin
  S := 'Honest Lincoln';
  Insert('Abe ', S, 8);  { 'Honest Abe Lincoln' }
end.


Természetesen nem minden problémát tudunk megoldani a standard alprogramok segítségével. Ilyenkor kihasználhatjuk azt a lehetőséget, hogy az indexeléssel hozzáférhetünk a string egyes karaktereihez. Tipikus megoldás, amikor egy for ciklussal lépdelünk végig a karaktereken, mint a következő példában:

Példa: Konvertáljunk át egy stringet nagybetűssé!
program Nagybetu;
uses Crt;
var S: string;
    I: byte;
begin
  ClrScr;
  ReadLn(S);
  for I := 1 to Length(S) do
    S[I] := Upcase(S[I]);
  WriteLn(S);
  ReadLn
end.


A karakterenkénti hozzáférést körültekintően végezzük! Intő példaként szolgáljon a következő hibás program:

Példa:
program Nagybetu_Hibas;
uses Crt;
var S, Nagy: string;
    I: byte;
begin
  ClrScr;
  ReadLn(S);
  for I := 1 to Length(S) do
    Nagy[I] := Upcase(s[I]);
{A nagy string hossza a program indulásakor 0,
  és nem változik}
  WriteLn(Nagy); {Egy üres stringet ír ki}
  ReadLn
end.


1. Adottak a következő deklarációk.
Var St :String;
    Ch :Char;
    B  :Byte;

Mely értékadás(ok) helyes(ek)?
A  St:=Ch+Ch;     B. Ch:=St;     C. B:=-111;
D. St:='-111';    E. St:=St[1];

2. Igaz, vagy hamis?
A. Egy karakterlánc hossza maximum 256 karakter lehet.
B. A karakterláncokra értelmezett a + művelet.
C. A Pos egy függvény.
D. ’ ATTILA' < 'ALFONZ'

3. Mit ír ki az alábbi program, és miért:
    var s: string;
        b, c: byte;
    begin
      s := 'Szép az idô';
      b := Pos('esik', s);
      c := b * Length(s);
      Writeln(c)
    end.

4. Adott a következő program:
Program P;
Var S:String;
    I:Byte;
Begin
  S:='AbLaK';
  For I:=1 To Length(S) Do
  If S[I]=UpCase(S[I]) Then
    Write(S[I]);
End.

Mi jelenik meg a képernyőn ?
A. AbLaK     B. ABLAK     C. ALK
D. aBlAk     E. ba


1. Olvasson be egy karakterláncot! Számolja meg, hány szóköz van benne, és cseréljük ki benne az összes szóközt a * karakterre!

2. Olvasson be ’*’ végjelig neveket. Számolja meg, hány István volt közöttük! Írja ki a leghosszabb és a legrövidebb nevet!

3. Írjon olyan ellenőrzött adatbevitelt, amely csak háromjegyű pozitív egész számokat fogad el! A program nem állhat le futási hibával.


2. A karakteres képernyő kezelése – a CRT unit

A Turbo Pascal nyelvben lehetőségünk van ún. egységek (unitok) létrehozására, amelyek különböző lefordított, tehát szintaktikailag hibátlan programelemeket (eljárásokat, függvényeket, definiált konstansokat, típusokat, változókat, stb.) tartalmazhatnak. A unitok elemeit a programjainkban használhatjuk, feltéve, ha a unit nevét a programunk elején a uses kulcsszó után megadjuk. A nyelv készítői a rendelkezésünkre bocsátottak számos egységet.
A CRT unit néhány elemét Ön már a II. tanegységben megtanulta, és azóta is alkalmazza. A Crt egység a karakteres képernyő, a billentyűzet valamint a hangszóró kezelését segítő függvényeket, eljárásokat tartalmazza. Mint az egységek többsége, a Crt unit is definiál konstansokat, változókat.
A teljesség igénye nélkül példákon keresztül nézzük meg a Crt unit főbb elemeit! Kérem, hogy az egyes eljárások, függvények általános szintaktikáját, leírását tanulmányozza a Helpben, vagy egy referenciakönyvben [2], vagy [3]! Kérem, hogy futtassa le, próbálja ki a példákat!

Színek
A karakteres képernyő általánosan használt üzemmódjában 16 karakterszínt (0..15 kóddal) és 8 háttérszínt (0..7 kóddal) használhatunk. A unit konstansok definiálásával könnyíti meg a színek használatát, a konstansok azonosítói a színek angol nevei. Néhány szín kódja és a definiált konstans:
fekete: 0, Black
fehér: 15, White
kék: 1, Blue
világoskék: 9, LightBlue
A Blink (értéke 128) konstans használatával a szöveg villogását érhetjük el.
A karakter, illetve a háttérszínt a TextColor, illetve a TextBackground eljárásokkal állíthatjuk be.

Példa:
uses Crt;
begin
  { Fekete háttér zöld karakterek }
  TextColor(Green);
  TextBackground(Black);
  WriteLn('Hello fiúk!');
  { Villogó világospiros karakterek szürkén }
  TextColor(LightRed+Blink);
  TextBackground(LightGray);
  WriteLn('Hello fiúk!');
  { Sárga karakterek kéken }
  TextColor(14); { Yellow = 14 }
  TextBackground(Blue);
  WriteLn('Hello fiúk!');
  NormVideo; { Eredeti beállítás }
  WriteLn('Vissza a normálhoz...');
end.

Képernyőkezelés
Mint azt már Ön tudja, a ClrScr eljárással letörölhetjük a képernyőt. Ezen most kicsit pontosítunk, hiszen minden képernyőkezelő programelem az aktív ablakra vonatkozik, ami alapértelmezés szerint az egész képernyő, de a Window eljárással megváltoztatható. A GotoXY eljárással a kurzort mozgathatjuk a képernyőn (pontosabban az aktív ablakban).

Példa:
program Ablak;
uses Crt;
begin
  {Az egész képernyőt töröljük}
  ClrScr;
  Window(20,5,40,10);
  TextBackground(Blue);
  {Az az aktív ablakot töröljük az aktív
    háttérszínnel}
  ClrScr;
  TextColor(Yellow);
  {Az aktív ablakon belül pozícionálunk}
  GotoXY(10,3);
  Write('Hello');
  ReadLn;
  NormVideo
end.

A GotoXY eljárással csinosabbá tehetjük például a menüs programjainkat.


Billentyűzetkezelés:
Ön már megtanulta és alkalmazta a ReadKey függvényt, melynek működését úgy írtuk le, hogy várakozik egy karakter leütésére, és a leütött karaktert adja vissza. Mindez csak akkor igaz, ha üres a billentyűzetpuffer, mert ha ott van még fel nem dolgozott karakter, akkor az lesz a visszatérési értéke. A KeyPressed függvény az "Ütöttek le billentyűt a klaviatúrán?" kérdésre válaszol. Visszatérési érteke logikai típusú: True, ha nem üres a billentyűzet puffer, egyébként False.

Példa:
uses Crt;
begin
  repeat
    { Egy billentyű leütéséig folytatja a kiírást }
    Write('Xx');
  until KeyPressed;
end.


Hang, késleltetés:
A Sound eljárás megszólaltat egy hangot a beépített hangszórón. A hangszórót a NoSound eljárással kapcsolhatjuk ki, egyébként a hang még a programból való kilépés után is szól. A be- és kikapcsolás közé helyezhetjük a Delay eljárást, amely a program végrehajtását késlelteti, azaz várakoztatja a programot egy általunk megadott ideig.

Példa:
uses Crt;
begin
  Sound(220);  { Hang }
  Delay(200);  { 2 tizedmásodpercig }
  NoSound;     { Leállítás, különben tovább szól}
end.

Nézzünk egy nagyobb példát a Crt unit elemeinek alkalmazására!

Példa: Mozgassunk egy téglalapot (egy kis képernyőt) benne egy szöveggel a képernyőn a kurzormozgató billentyűk segítségével!
program CrtPl;
uses Crt;
var  x1, x2, y1, y2: byte;
     c: char;
begin
  x1:=35; y1:=12; x2:=45; y2:=16;
  repeat
    {A régi ablak törlése}
    TextBackground(black);
    ClrScr;
    {Új ablak és a szöveg megjelenítése}
    Window(x1, y1, x2, y2);
    TextBackground(blue);
    TextColor(red);
    ClrScr;
    GotoXY(3, 3);
    WriteLn('szoveg');
    {Várakozás egy billentyű leütésére}
    c := ReadKey;
    {Ha a billentyűzetnek két bájtos kódja van (az
     első bájt #0), a második bájt beolvasása.
     Ilyenek a kurzormozgató billentyűk.}
    if c = #0 then
      begin
        c := ReadKey;
        case c of
          {Felfele nyíl}
          #72: begin Dec(y1); Dec(y2) end;
          {Felfele nyíl}
          #80: begin Inc(y1); Inc(y2) end;
          {Jobbra nyíl}
          #77: begin Inc(x1); Inc(x2) end;
          {Balra nyíl}
          #75: begin Dec(x1); Dec(x2) end;
        end;
      end
    {Kilépés ESC-re}
  until c = #27;
  NormVideo;     {Eredeti színek visszaállítása}
  ClrScr
end.



5. Az alábbi azonosítók (A - G) milyen programelemek (I-III)?
A. Sound    B. Black    C. ClrScr    D. ReadKey   E. KeyPressed
F. Window    G. Red
I. definiált konstans    II. eljárás    III. függvény

6. Az aktuális karakterszínt kékre szeretnénk beállítani. Melyik megoldás(ok) a helyes(ek)?
A. Color := Blue;  B. Color := 1;
C. SetColor(Blue); D. SetColor(1)
E. Color := SetColor(Blue)
F. Color := SetColor(1)

7. Milyen típusú visszatérési értéke van a következő függvényeknek?
A. KeyPressed    B. ReadKey

4. Írjon programot, amely kirajzol a képernyő közepére egy 10*10-es csillagokból álló téglalapot!

5. Írjon programot, amely egy rövid dallamot megszólaltat!

6. Készítse el a II. tanegység 11. ellenőrző feladatának programját úgy, hogy használja a Crt unit eszközeit (pl. színek, Window, GotoXY)!



3. A Turbo Pascal grafikája – a Graph unit
Ön bizonyára már tapasztalta a különbséget a képernyő karakteres és grafikus üzemmódja között. A DOS operációs rendszeren működő programok zöme, így a Turbo Pascal fejlesztői rendszer, és az eddig írt programjai is karakteres üzemmódban kezelik a képernyő, míg pl. a Windows operációs rendszer grafikus üzemmódban.
Ha programunkban rajzolni szeretnénk a képernyőre, akkor azt át kell kapcsolnunk grafikus üzemmódba. A grafikus képernyőn külön-külön hozzáférhetünk az egyes képpontokhoz. Ismernünk kell (illetve bizonyos határok között meghatározhatjuk) a képernyőnk felbontását (a VGA üzemmódban a legnagyobb felbontás 640x480), valamint azt, hogy hány színt használhatunk (az előbb említett felbontásnál 16 színt). A (0, 0) koordinátájú képpont a képernyő bal felső sarkában található.
A Turbo Pascal grafikai eszközeit a Graph unit tartalmazza. Ez az egység a Crt unithoz képest jóval több elemet tartalmaz, ismét felhívjuk a figyelmét a referenciakönyv, Help használatára. Kurzusunk keretén belül Ön belekóstol a grafikai elemek alkalmazásába, és ez remélhetőleg hasznosnak bizonyul a haladóbb programozási tanulmányaiban, munkájában is.

Inicializálás
Pascal programunk indulásakor a képernyő karakteres üzemmódja az alapértelmezett. Ha rajzolni szeretnénk, át kell kapcsolnunk grafikus üzemmódra, azaz inicializálnunk kell a grafikus képernyőt. Erre egy lehetséges megoldást látunk az alábbi példában:

Példa:
uses Graph;
var Meghajto, Uzemmod: integer;
begin
  Meghajto := Detect; {Automatikusan beállítja
      grafikus üzemmódot a legnagyobb
      felbontással.}
  {Inicializálás}
  InitGraph(Meghajto, Uzemmod, 'C:\TP70\BGI');
  If GraphResult <> 0 then
    Begin
      {Nem sikerült az inicializálás, kilépés a
       programból}
    WriteLn('Grafikus hiba!'); ReadLn;
    Halt  {Megállítja a program végrehajtását}
  end;
   ...{Grafika használata}
  CloseGraph    {Grafikus képernyő bezárása}
End

Az InitGraph eljárás utolsó paramétere azon könyvtár elérési útját tartalmazza, amelyben a Turbo Pascal rendszernek a grafikus hardvert kezelő illesztőprogramjai, állományai találhatóak (BGI – Borland Graphics Interface). Ha ezt hibásan adja meg, akkor a programjának nem sikerül inicializálnia a grafikát. A másik ok az lehet, hogy a fejlesztői környezet Options/Directories menüpontjában rosszul van megadva a Pascal Unitokat tartalmazó könyvtár neve.

Színek:
16 színű üzemmódban ugyanazokat a konstansokat használhatjuk, mint a Crt unitban. Az aktuális színt, illetve háttérszínt a SetColor, illetve a SetBkColor eljárásokkal állíthatjuk be. Általánosan érvényes, hogy az egyes grafikai beállításokat (szín, vonalstílus, kitöltési stílus stb.) függvényekkel lekérdezhetjük, valamint eltárolhatjuk a unitban deklarál típusú változókban. A beállítások egyes értékeire konstansokat definiál a unit. Kérem próbálja ki az alábbi programot (természetesen az InitGraph eljárást megfelelően paraméterezve)!

Példa:
uses
  Crt, Graph; {Két unit eszközeit is használjuk}
var
  GraphDriver, GraphMode : Integer;
begin
  GraphDriver := Detect;
  InitGraph(GraphDriver, GraphMode, '');
  if GraphResult <> grOk then Halt(1);
  Randomize;
  repeat
    SetColor(16);
    LineTo(Random(640), Random(480));
  until KeyPressed;
end.


Rajz:
Csak néhányat említenénk a számos eszköz közül. Vonalat húzhatunk a Line, LineTo és LineRel eljárásokkal. Lényeges tudnunk, hogy az utóbbi kettő mozgatja az ún. grafikus kurzort, tehát azt a képzeletbeli pontot, ahonnan a következő rajzolás indul. A referenciakönyv és az alábbi példa segítségével értelmezze a működésüket:

Példa:
program Haromszogek;
uses Graph;
var Gd, Gm: integer;
begin
  Gd := Detect;
  InitGraph(Gd, Gm, 'C:\regid\bp\bgi');
  {1. háromszög}
  Line(100,200,200,200);
  Line(200,200,200,100);
  Line(200,100,100,200);
  {2. háromszög}
  MoveTo(300,200); {Mozgatja a grafikus kurzort}
  Lineto(400,200);
  Lineto(400,100);
  Lineto(300,200);
  {3. háromszög}
  MoveTo(500,200);
  LineRel(100,0);
  LineRel(0,-100);
  LineRel(-100,100);
  readln;
  CloseGraph
end.

A Circle eljárással kört, a Rectangle eljárással pedig téglalapot rajzolhatunk. A PutPixel eljárás egy képpontot „gyújt” ki.
Készíthetünk kitöltött rajzot is, például a Bar, vagy a FloodFill eljárásokkal. Ez utóbbi érdekesen működik: egy a paramétereként megadott pontból kiindulva folytatja az aktuális kitöltési színnel és mintával a kitöltést egészen addig, amíg a szintén paraméterként megadott színű határba nem ütközik.

Példa:
program Haromszog;
uses Graph;
var Gd, Gm: integer;
begin
  Gd := Detect;
  InitGraph(Gd, Gm, 'C:\TP70\BGI');
  SetColor(LightBlue);
  MoveTo(100,400);
  LineRel(300,0);
  LineRel(0,-300);
  LineRel(-300,300);
  {Pirossal kitöltjük a kék háromszöget}
  SetFillStyle(1, Red);
  FloodFill(300,300,LightBlue);
  readln;
  CloseGraph
end.



Szöveg
A grafikus képernyőre az OutText és az OutTextXY eljárásokkal írhatunk. Ezen eljárások, ahogy a nevük is mutatja, csak szöveges adat (string és char) kiírására képesek. Ön az Str eljárás kapcsán már találkozott az OutTextXY eljárással, kérem lapozzon vissza!

Az alábbi Web címen találhat egy összetettebb példát az eddig említett, valamint új grafikai elemek használatával:
http://zeus.nyf.hu/~akos/pascal/pasframe.htm (10. fejezet, példa)
A gyakorló és ellenőrző feladatok megoldásához is használjon referenciakönyvet, vagy Helpet!

8. Az alábbi azonosítók (A - G) milyen programelemek (I-III)?
A. SetColor    B. Blue    C. InitGraph    D. Red   E. GetColor
F. Circle
I. definiált konstans    II. eljárás    III. függvény

9. Mik a különbségek a Line és a LineTo eljárások között?

10 Milyen típusú az S változó, mi jelenik meg a képernyőn?
X := 2/3;
Str(X:5:2, S);
OutTextXY(10, 10, S);

7. Véletlen színnel, sugárral és elhelyezkedéssel rajzoljunk köröket a képernyőre.

8. Készítsen programot, amely kiírja az egész számokat 1-től 30-ig a grafikus képernyőre!

9. Írjon programot, mely egy kitöltött szabályos hatszöget rajzol ki.

Összefoglalás

Ön e tanulási egységben megtanulta a karakterlánc (String) adattípus jellemzőit, használatát, és ezáltal képessé vált olyan programok készítésére, amelyek szöveges adatokat dolgoznak fel.
A programozói munka során elengedhetetlen a fejlesztői környezet Help rendszerének, valamint a referencia könyveknek a használata. Ön a CRT és a Graph unit fontosabb elemeinek alkalmazása során gyakorolta ezen segítségek használatát, és egyben növelte programozási készségét, rutinját.


Ajánlott irodalom

[1] Angster Erzsébet: Programozás tankönyv I, 4KÖR Bt., 1995
[2] Angster-Kertész: Turbo Pascal 6.0, Angster Erzsébet- Kertész László, 1993
[3] Angster-Kertész: Turbo Pascal ’A’..’Z’ referenciakönyv
[4] Benkő Tiborné és tsi: Programozzunk Turbo Pascal nyelven, ComputerBooks, 2001
[5] Angster-Kertész: Turbo Pascal feladatgyűjtemény, 4KÖR Bt., 1998

Gyakorló feladatok megoldása

1. A, D, E

2. hamis, igaz, igaz, hamis

3. nullát, mivel a Pos('esik', s) függvény nulla értékkel tér vissza

4. C

5. A. II,   B I,   C. II,   D. III,   E. III,   F II,   G I

6. C, D

7. A. logikai    B. karakter

8. A. II,   B. I,   C. II,   D. I,   E. III.   F. II

9. A Line esetében meg kell adni a kezdő és a végpontot is, míg a LineTo a kurzor aktuális pozíciójából húz egy vonalat a megadott pontba. A LineTo mozgatja a grafikus kurzort.

10. String, 0.67

V. tanulási egység
Eljárások, függvények

Ha Ön e tanulási egység tananyagát elsajátítja, képes lesz:
- saját eljárásokat és függvényeket írni,
- azokat használni a programjaiban,
- egyszerű rekurzív algoritmusokat elkészíteni.

Tartalom
Bevezetés
1. Eljárás
1.1 Szerkezete, hívása
1.2 Paraméterek
1.3 Lokális és globális változók, információ csere
1.4 Egymásba ágyazás
2. Függvények
3 Rekurzió
Összefoglalás
Ajánlott irodalom
Gyakorló feladatok megoldása

Bevezetés

Ön a kurzus során már számtalanszor használt eljárásokat, függvényeket. Ezeket az ún. standard, vagy szabványos alprogramokat a Turbo Pascal rendszer programozói készítették, és Ön felhasználhatja őket saját programjaiban. Megtanulta, hogy az eljárás egy tevékenység sorozatot hajt végre (pl. letörli a képernyőt, beolvas a billentyűzetről), míg a függvény egy értéket állít elő. Az eljárás hívása utasítás szerűen történik, a függvényé pedig egy kifejezésben. Láttuk azt is, hogy az alprogramok paraméterek segítségével kommunikálhatnak a hívó programmal.
Lehet, hogy felmerült már Önben az igény, hogy saját maga készítsen eljárásokat, függvényeket. Mikor írunk eljárást, vagy függvényt? Akkor, ha:
- bizonyos tevékenység többször előfordul a programunkban, vagy
- egy nagy programot tagolni, strukturálni szeretnénk.

Felfoghatjuk az eljárások, függvények készítését, szerepét úgy is, hogy bizonyos részeket kiemelünk programunkból, külön eljárásként, függvényekként deklaráljuk őket, majd a programban (főprogramban) az eredeti helyükön meghívjuk új alprogramjainkat.

Ebben a tanegységben először az eljárások készítésének, használatának főbb jellemzőit tekintjük át, majd megnézzük, hogy milyen különbségek vannak ehhez képest a függvények esetén. Végül szó lesz az alprogramok egy érdekes alkalmazásáról, a rekurzióról.

1. Eljárás

Az eljárás tehát egy olyan alprogram, amely egy tevékenység sorozatot hajt végre.

1.1 Szerkezete, hívása

Saját eljárásainkat a program deklarációs részében kell elhelyeznünk, azaz deklarálnunk. Bár nem kötelezően, de általában a változó deklarációk után következnek.
Az eljárás szerkezete hasonló magának a programnak a szerkezetéhez. Három fő részből áll:
- eljárásfej
- deklarációs rész
- végrehajtó rész.
Nézzük meg az eljárások deklarálását, szerkezetét és használatát (hívását) példákon keresztül.

Példa:
program Eljaras;
uses Crt;
var X, Y: real;

procedure Vonal;
  var I: integer;
  begin
    WriteLn;
    for I := 1 to 80 do
      Write('-');
    WriteLn;
  end;

procedure Hang;
  begin
    Sound(1000);
    Delay(1000);
    NoSound;
  end;

begin
ClrScr;
  Write('Az egyik szám: '); ReadLn(X);
  Write('A másik szám: '); ReadLn(Y);
  Vonal;
  WriteLn('A két szám szorzata: ', X * Y:10:2);
  Vonal;
  if Y <> 0 then
      WriteLn('A két szám hányadosa: ',X/Y:10:2)
  else
    begin
      Hang;
      WriteLn('Nullával nem osztunk!')
    end;
  Vonal;
  ReadLn;
end.

A Eljaras nevű programunk deklarációs részében két saját eljárást deklaráltunk, az egyik egy szaggatott vonalat húz, a másik egy figyelmeztető hangot szólaltat meg.
Az eljárásfej a procedure kulcsszóval kezdődik, majd ezt követi az eljárás neve (azonosítója), és végül a paraméterek. A példánkban szereplő eljárások paraméter nélküliek. A Hang eljárásnak hiányzik a deklarációs része. A Vonal működése során használ egy változót (I), ezt a saját deklarációs részében deklaráltuk. Hasonlóan az egész programhoz, a végrehajtó részt a begin – end utasítás-zárójel fogja közre, de itt az end után pontosvesszőt kell tennünk.
Az eljárásokat többször meghívtuk a programunkban.

1.2 Paraméterek

Előfordulhat, hogy a programunkban nem mindig azonos hosszúságú vonalat akarunk húzni, illetve azonos magasságú és időtartamú hangot akarunk megszólaltatni. Ekkor használhatunk paramétereket, amelyek segítségével befolyásolhatjuk az eljárások működését. Nézzük a módosított példánkat.

Példa:
program Eljaras2;
uses Crt;
var X, Y: real;

procedure Vonal(Hossz: integer);
  var I: integer;
  begin
    WriteLn;
    for I := 1 to Hossz do
      Write('-');
    WriteLn;
  end;

procedure Hang(Magassag, Hossz: Word);
  begin
    Sound(Magassag);
    Delay(Hossz);
    NoSound;
  end;

begin
ClrScr;
  Write('Az egyik szám: '); ReadLn(X);
  Write('A másik szám: '); ReadLn(Y);
  Vonal(80);
  WriteLn('A két szám szorzata: ', X * Y:10:2);
  Vonal(40);
  if Y <> 0 then
    begin
      WriteLn('A két szám hányadosa: ',X/Y:10:2);
      Hang(1000,1000);
    end
  else
    begin
      Hang(100,10000);
      WriteLn('Nullával nem osztunk!')
    end;
  Vonal(80);
  ReadLn;
end.

Mindkét eljárásfejben a név után zárójelben felsoroltuk a paramétereket, és megadtuk a típusukat, hasonlóan, mint amikor változókat deklarálunk. Ha különböző típusú paramétereket akarunk felvenni, akkor pontosvesszővel kell elválasztanunk őket. Ön az előző tanegységben megtanulta a referenciakönyvek, illetve a Help használatát. Ott az alprogramoknak az eljárásfejét adják meg, pl.:

Procedure GotoXY(X, Y: Byte);

Az eljárások deklarációja során felvett paramétereket formális paramétereknek nevezzük. Ezeket, mint változókat használhatjuk az eljárásban, ezek segítségével írjuk le az eljárás tevékenységét. Például a Vonal eljárás for ciklusának végértéke a Hossz paraméter. Híváskor a formális paraméterek helyére konkrét objektumokat, aktuális paramétereket írunk. A példánkban a Vonal eljárás esetén a Hossz helyére 80-at, illetve 40-et. A paraméterek tehát az eljárás és az őt hívó programrész közötti adatcserét, kommunikációt szolgálják. Ezt az adatcserét paraméterátadásnak nevezzük. Az aktuális és a formális paramétereknek meg kell egyezniük számban, sorrendben és típusban.

Ön az eddigi tanulmányai során látta, hogy egy eljárás megváltoztathatja a híváskor behelyettesített aktuális paraméterének az értékét. A IV. tanegységben említettük, hogy ilyen esetben a formális paramétert deklaráláskor egy var szócskának kell megelőznie. Nézzünk erre is egy példát:

Példa:
program Eljaras3;
uses Crt;
var A, B: integer;

procedure CsereRossz(A, B: integer);
  var C: integer;
  begin
    C := A;
    A := B;
    B := C;
  end;

procedure CsereJo(var A, B: integer);
  var C: integer;
  begin
    C := A;
    A := B;
    B := C;
  end;

begin
ClrScr;
  Write('Az egyik szám: '); ReadLn(A);
  Write('A másik szám: '); ReadLn(B);
  CsereRossz(A, B);
  WriteLn(A:5, B:5);
  CsereJo(A, B);
  WriteLn(A:5, B:5);
  ReadLn;
end.

Olyan eljárást akartunk írni, amely megcseréli (tehát megváltoztatja) a két paraméterének az értékét. A rossz megoldásban nem használtunk var-t a formális paraméter előtt. Próbálja ki a programot! Megjegyezzük, hogy annak nincs jelentősége, hogy mind a főprogram változóit, mind az egyes eljárások paramétereit A-nak, illetve B-nek neveztük.

Nézzük kicsit mélyebben, hogy mi a különbség a két fajta paraméterátadás között.


Érték szerinti paraméterátadás.

A deklarációban a formális paraméter előtt nincs var. Ekkor az aktuális paraméter értéke kerül át a formális paraméterbe. Az eljárás minden egyes hívásakor a rendszer tárterületet rendel az ún. verem memóriában a formális paraméterekhez, és ide másolja be az aktuális paraméterek értékeit. Az eljárás végeztével ez a terület felszabadul. Az aktuális paraméter értékét az eljárás nem változtathatja meg, így ez csak bemenő paraméter. Az aktuális paraméter kifejezés lehet (pl. az Eljaras2 példában a Vonal hívásakor 80, ami egy egyetlen konstansból álló kifejezés).

Cím szerinti paraméter átadás

A deklarációban a formális paraméter elé var -t írunk. Az aktuális paraméter címe kerül át a formális paraméterhez, ha változik a formális paraméter, akkor változik az aktuális is. Ezáltal egyaránt használhatjuk be- és kimenő paraméterként is. Az aktuális paraméter csak változó lehet (pl. az Eljaras3 példában a CsereJo hívásakor csak változót adhatunk meg aktuális paraméterként).
Nézzünk egy olyan példát, ahol az eljárás mindkét típusú paraméterrel rendelkezik.

Példa: Írjunk és használjunk egy olyan eljárást, amely beolvas egy egész számot, és a beolvasás előtt egy üzenetet megjelenít!
program Eljaras4;
uses Crt;
var A, B, C: integer;

procedure BeEgesz(Prompt: string; var Szam:
                   integer);
  begin
    Write(Prompt);
    ReadLn(Szam)
  end;

begin
  ClrScr;
  BeEgesz('Az egyik szám: ', A);
  BeEgesz('A másik szám: ', B);
  C := A + B;
  WriteLn('A két szám összege: ',C);
  ReadLn;
end.


1.3 Lokális és globális változók, információ csere
Az eljárás működése során használhat változókat, amelyeket a saját deklarációs részében deklarálhatunk. Ezen változókat lokális, helyi változóknak nevezzük. Ezek a program más részein nem ismertek. (Így különböző eljárásokban, illetve a főprogramban előfordulhatnak azonos nevű változók, amelyeknek azonban semmi közük egymáshoz.) Úgy is mondhatjuk, hogy a lokális változók érvényességi tartománya, más elnevezéssel hatásköre azon eljárásra korlátozódik, ahol deklaráltuk. A hatáskör mellett a másik fő jellemzője az élettartam. A lokális változókhoz (az eljárás paramétereihez hasonlóan) a rendszer a veremben rendel tárterületet, dinamikus módon, azaz csak akkor „él” a változó, ha az eljáráson van a vezérlés. Így a lokális változók értéke az eljárás két hívása között elvész.

Egy eljárásra nézve globális változó egy őt tartalmazó eljárásban (az eljárások egymásba ágyazhatók, ld.1.4 fejezet) vagy a főprogramban deklarált változó. Ezt az eljárás ismeri, hacsak nem deklaráltunk egy vele azonos nevű lokális változót vagy az eljárásnak nincs egy vele azonos nevű paramétere. Ekkor a lokális változó "eltakarja" a globálisat. A globális változó értéke természetesen ilyenkor sem vész el, programnév.változónév-vel hivatkozhatunk rá.

Egy példa a lokális és globális változók értelmezésére.

program Pelda;

var a, b, c: integer;
    x, y, z: real;

procedure P1(x:real);
  var a: char;
      s: string;     {lokális változók}
  begin
   {globális változók: b, c, y, z  (az x, a nem)}
  end;

procedure P2;
  begin
   {imeri a főprogram összes változóját, mint
   globális változót, nem ismeri
   a P1 s, a változóit}
  end;

begin

end.

Összefoglalva elmondhatjuk, hogy egy alprogram kétféle módon kommunikálhat az őt hívó programegységgel,
- a paramétereken keresztül,
- a globális változók segítségével.
Egy eljárásban és természetesen a főprogramban nem csak változókat deklarálhatunk (hanem pl. újabb eljárásokat), ezért általánosan globális és lokális deklarációkról beszélhetünk.

1.4 Egymásba ágyazás

A Pascal nyelv befelé strukturált, az alprogramokat (eljárásokat, függvényeket) egymásba ágyazhatjuk, az alprogram deklarációs részében is lehet alprogram. Ezért fontos tisztán látnunk, hogy a főprogramból illetve egy alprogramból mely eljárásokat, függvényeket hívhatjuk meg:
- A program illetve egy eljárás meghívhatja (ismeri) azokat az alprogramokat, melyeket a program vagy az adott alprogram deklarációs részében deklaráltunk, de azok alprogramjait már nem.
- Egy alprogram meghívhatja az ugyanazon deklarációs részben (ahol őt deklaráltuk) korábban deklarált alprogramokat.
- Egy alprogram meghívhatja az őt tartalmazó eljárásokat.
- Egy alprogram meghívhatja saját magát (ld. rekurzió).

Nézzük a fenti eseteket egy példán keresztül.

, A program illetve egy eljárás meghívhatja (ismeri) azokat az alprogramokat, melyeket a program vagy az adott alprogram deklarációs részében deklaráltunk, de azok alprogramjait már nem.
, Egy alprogram meghívhatja az ugyanazon deklarációs részben (ahol őt deklaráltuk) korábban deklarált alprogramokat.
 Egy alprogram meghívhatja az őt tartalmazó eljárásokat.
 Egy alprogram meghívhatja saját magát.

program Pelda;
...
procedure p1;
  procedure p1_1;
    begin
      ...
      p1;   { eset}
      p1_1; { eset}
      ...
    end;
  procedure p1_2;
    begin
      ...
      p1_1; { eset}
      p1;   { eset}
      p1_2; { eset}
      ...
    end;
  begin {p1 eljárás végrehajtó része}
    ...
    p1_1;   { eset}
    p1_2;   { eset}
    p1;     { eset}
    ...
  end;

begin
  ...
  p1;       { eset}
  ...
end.


2. Függvények

A függvényekre is érvényesek az eljárások esetén megtanultak, a következő különbségekkel. A függvény feladata egy érték előállítása. Ezt az értéket a függvény nevéhez rendeljük, a függvény törzsében kell szerepelni legalább egy értékadó utasításnak, amelyben a függvény neve a baloldalon áll. (Vigyázzunk, ha a jobboldalon szerepeltetjük a függvény nevét, akkor az már rekurziót jelent.)
A függvényt egy kifejezésben hívhatjuk meg, pl. egy értékadó utasítás jobboldalán. Szerkezete megegyezik az eljáráséval azzal a különbséggel, hogy a function kulcsszó után deklaráljuk, és még meg kell határoznunk a visszatérési érték típusát is a formális paramétereket követően egy kettőspont után. Például:

function Tangens(Alfa: real): real;
begin
  if cos(Alfa) <> 0 then
    Tangens := Sin(Alfa) / Cos(Alfa)
  {A kiszámolt érték hozzárendelése a függvény
   nevéhez}
end;

Példa: Írjunk függvényt, amely egy karakterláncot nagybetűssé konvertál!
Program Nagybetu;
uses Crt;
var Szoveg: string;

function Upper(S: string): string;
  var i: integer;
  begin
    for i := 1 to Length(S) do
      S[i] := UpCase(S[i]);
    Upper := S;
  end;

begin
  ClrScr;
  Write('Kérek egy szöveget: ');
  ReadLn(Szoveg);
  Szoveg := Upper(Szoveg);
  WriteLn(Szoveg);
  ReadLn;
end.


3 Rekurzió

Ha egy alprogram saját magát meghívja, akkor rekurzióról beszélünk. A rekurzió alkalmazásának egyik területe, amikor úgy oldunk meg egy problémát, hogy visszavezetjük egy egyszerűbb esetre, majd ezt addig folytatjuk, míg el nem jutunk a triviális esetig. A módszer a matematikai indukción alapszik.
A megoldás lépései:
1. Megkeressük azt a legegyszerűbb esetet, ahol a megoldás már magától értetődő - triviális eset. Ekkor áll le a rekurzív hívások sorozata.
2. Megvizsgáljuk, hogy ismételt egyszerűsítésekkel hogyan juthatunk el a triviális esethez. (Az általános esetet visszavezetjük az eggyel egyszerűbbre.)
Vegyük például a faktoriális kiszámítását (N! = 1*2*3*...*N):
- Triviális eset: 0! = 1
- Egyszerűsítés: N! = N*(N-1)!
Ezzel a problémát megoldottuk, már csak kódolnunk kell.
Bár a feladat kitűnő példa a rekurzív algoritmusra, az iterációs (ciklussal történő) megoldás jobb, mivel az ismételt függvényhívások időigényesek. Nézzük mind a két megoldást:

program Faktorialis;
uses Crt;
var N: integer;

{Iterációs megoldás}
function  FaktIter(N: integer): longint;
  var i: integer;
      fakt: longint;
  begin
    fakt := 1;
    for i: = 2 to N do fakt := fakt * i;
    FaktIter := Fakt
  end;

{Rekurzív megoldás}
function FaktRek(N: integer): longint;
  begin
    if N = 0 then
      {Triviális eset}
      FaktRek := 1
    else
      {Visszavezetés az egyszerűbbre}
      FaktRek := n * FaktRek(n-1)
  end;

begin
  ReadLn(N);
  WriteLn(FaktIter(N));
  WriteLn(FaktRek(N));
  ReadLn
end.


1. Melyik tulajdonság vonatkozik az eljárásokra, és melyik a függvényekre?
A. egy értéket állít elő,    B. egy tevékenység sorozatot hajt végre
C. utasításszerűen hívható    D. egy kifejezésben hívjuk

2. Mi lehet az oka az alprogramok használatának?

3. Melyik paraméterátadásra jellemző: csak bemenő paraméter, azaz az aktuális paramétert az alprogram nem változtathatja meg, az aktuális paraméter kifejezés lehet.

4. Megegyezhet-e egy lokális változó neve egy a főprogramban deklarált változóéval?

5. Miben kell megegyezniük az aktuális és a formális paramétereknek?
A. számban    B. sorrendben    C. típusban

6. Adott a következő program:
Var X, Y :Integer;

Procedure Csokkent(Var Szam:Integer;
                       Mennyivel:Integer);
  Begin
    Szam:=Szam-Mennyivel;
  End;

Begin
  X:=10;
  Y:=X;
  Csokkent(X,X-7);
End.

Mennyi lesz X és Y értéke a program végén?
A. 10, 10    B. 7, 10    C. 7, 7    D. 10,7
7. Adottak a következő deklarációk és egy eljárásfej:
Var X,Y:Real;
    SZOV:String;

Procedure P(A:Real;Var B:Real;Var S:String);

A P eljárás mely hívásai helyesek:
A. P(X+Y,Y,SZOV); B. P(Y,VAR X, SZOV);
C. P(X,Y,'BUBU'); D. P(X,X+Y,SZOV);
E. P(3.8,0.5,SZOV);




1. Adottak a következő deklarációk, és egy függvényfej:
Var A,Y:Real;
    S1,S2:String;
    B:Boolean;

Function P(X:Real;S:String):Boolean;

A függvénynek mely hívásai helyesek:
A. B:=P(A+Y,S1+S2);
B. Writeln(P(3.5,'Tisza'):10);
C. B:=P(A,B);
D. B:=P(2.2,'BUBA') Or P(3.3,'BUBU');
E. A:=P(0,S1);

2. Tekintse a következő programot:
Program Teszt;
Var X:Byte;
    Ch:Char;

Procedure P(Var C:Char; A:Byte);
  Begin
    C := UpCase(C);
    Inc(A, 3);
  End;

Begin
  X := 1; Ch := 'a';
  P(Ch, X);
  WriteLn(X:2, Ch:2);
End.

Mi jelenik meg a képernyőn?
A. 4 A    B. 4 a    C. 1 A    D. 2 A

3. Mit ír ki a következő program?
var A: integer;

procedure P(var B:integer);
  var A: integer;
  begin
    A := 5;
    B := B + A;
  end;

begin
  A := 6;
  B := 10;
  P(B);
  WriteLn(A:2, B:2);
end.

A. Semmit, mert szintaktikai hibás
B. 5, 15     C. 6, 15    D. 5, 10    E. 6, 10

4. Módosítsa úgy a Vonal eljárást, hogy a „vonalhúzó” karaktert is meg lehessen adni! Hívása pl. Vonal(20, '*');

5. Írjon eljárást, amely a képernyő egy adott pozíciójába kiír egy szöveget és egy egész számot!
Hívása pl. Kiir(15, 5, 'Az eredmény: ', A)

6. Írjon egy DownCase nevű függvényt, amely a paraméterként megadott karaktert kisbetűvé konvertálja, ha az nagybetű, egyébként pedig az eredeti karaktert adja vissza! (Az UpCase párja.)

7. Írjon eljárást, amely kirajzol egy szabályos háromszöget a grafikus képernyőre. Paraméterei: a háromszög oldal hosszúsága és súlypontjának a koordinátái. (A grafikát az eljárásnak nem kell inicializálnia.)

8. Írjon függvényt, amely két egész paraméterének a maximumát adja vissza!

9. Írjon eljárást egy egész szám ellenőrzött beolvasására. (A nem szám formátumú adat begépelésére ne álljon le futási hibával a program.)

10. Írjon rekurzív függvényt pozitív egész kitevőjű hatvány kiszámítására!


Összefoglalás
Ön már egyre nagyobb programozási rutinra és gyakorlatra tett szert, és így programjai is egyre összetettebbekké, hosszabbakká válnak. Nagyobb programok esetén a jobb áttekinthetőség, vagy az ismétlődő részek kiváltása érdekében célszerű alprogramokat, azaz eljárásokat, függvényeket létrehozni. Ön ezen tanegység megtanulása után képes az eljárások és a függvények deklarálására, használatára.

Ajánlott irodalom

[1] Angster Erzsébet: Programozás tankönyv I, 4KÖR Bt., 1995
[2] Angster-Kertész: Turbo Pascal 6.0, Angster Erzsébet- Kertész László, 1993
[3] Angster-Kertész: Turbo Pascal ’A’..’Z’ referenciakönyv
[4] Benkő Tiborné és tsi: Programozzunk Turbo Pascal nyelven, ComputerBooks, 2001
[5] Angster-Kertész: Turbo Pascal feladatgyűjtemény, 4KÖR Bt., 1998

Gyakorló feladatok megoldása

1. Eljárás: B, C    függvény: A, D

2. - Egy nagyobb program tagolása, áttekinthetőbbé tétele.
- Az ismétlődő részek kiváltása

3. Az érték szerinti paraméterátadásra

4. Igen, és ekkor a főprogram azonos nevű változóját csak a programnévvel kiegészítve használhatom.

5. Mindháromban

6. B

7. A

VI. tanulási egység
Tömbök

Ön ebben a tanulási egységben megtanulja a tömb adattípus jellemzőit, és a hozzá kapcsolható alapalgoritmusokat.
Ha Ön e tanulási egység tananyagát elsajátítja, képes lesz:
- a feladat adatainak megfelelő tömb struktúrát kiválasztani,
- azt magabiztosan használni, kezelni;
- saját típusokat definiálni
- az alapalgoritmusokat beilleszteni saját programjaiba.

Tartalom
Bevezetés
1 Fogalma, deklarációja, dimenzió
2 Típusdefiníció
3 Indexelés, műveletek, feldolgozása
4 A tömbökkel kapcsolatos alapfeladatok
Összefoglalás
Ajánlott irodalom
Gyakorló feladatok megoldása


Bevezetés

Ön az eddig írt programjaiban csak egyszerű (skalár) adattípusokat használ. A programozási feladatokban gyakran szükségünk van arra, hogy nagy számú, illetve összetett szerkezetű adatokat kezeljünk (pl. egy iskola tanulóinak a személyi adatait, egy bolt árukészletének az adatai, stb.). Ön eddigi tanulmányai során talán már tanul is olyan adatszerkezetekről, amelyek ilyen adatok tárolására, kezelésére képesek (tömb, tábla, lista, stb.). A programozási nyelvek eszközöket biztosítanak ezen adatszerkezetek megvalósítására. A legtöbb nyelv, és így a Turbo Pascal is definiálja a tömb adattípus, amely a legfontosabb adatszerkezetet valósítja meg. A tömb típus jellemzőiről, használatáról, a hozzá kapcsolódó algoritmusokról lesz szó ebben a tanegységben.

1. Fogalma, deklarációja, dimenzió

Képzeljük el, hogy nagy számú adatot, például egy 30 fős osztály tanulóinak a matematika jegyeit kell egyszerre kezelnünk programunkban. Ön ezt eddigi ismereteivel csak úgy tudná megoldani, hogy felvenne 30 változót, amelyekkel egyesével kellene műveleteket végeznie (pl. beolvasás, számítások stb). Ön is látja, hogy ez nem járható út. A tömb típus használatával mind a változó deklaráció, mind az adatok kezelése leegyszerűsödik, például ciklusokat használhatunk az egyes osztályzatokon végzett egyforma műveletekre.
A tömb adott számú, azonos típusú elemet tartalmazó összetett (strukturált) adattípus. Példánkban az osztályzatokat egy olyan tömbben tárolhatjuk, amelynek 30 egészszám típusú eleme van. A tömb elemeinek a sorrendje kötött, minden elemnek van egy indexe, az elemekhez közvetlenül hozzáférhetünk, egy tömbelemre az indexével hivatkozhatunk.
Példákon keresztül nézzük meg a tömb típusú változók deklarálásának a szabályait:

      var Matek: array[1..30] of byte;

Deklaráltuk az osztályzatok kezeléséhez szükséges változót. A Matek tömbben programunk 30 darab byte típusú számot képes tárolni. A deklarációt az array kulcsszóval kezdjük, majd ezután szögletes zárójelben következik a töm indextípusa, amely leggyakrabban, mint a példánkban is intervallum típus. Lényeges, hogy az indextípus csak sorszámozott lehet. Az indextípus meghatározza a tömb elemeinek a számát, amely a program futása közben nem változhat. A deklarációt az of kulcsszót követően a tömb elemei típusának, az alaptípusnak a megadása zárja le.
További példák:

      var Nevsor: array[1..30] of string;

A Nevsor tömbben 30 karakterláncot helyezhetünk el.

      Var Pont: array[4..15] of byte;

A Pont tömbben egy kosárlabdacsapat tagjai által dobott pontszámot akarjuk tárolni. Az egyes játékosokra a mezszámuk alapján akarunk hivatkozni. A számozás a kosárlabdában 4-gyel kezdődik, így az indexelést mi is 4-től kezdtük, amit természetesen megtehetünk. A Pont tömbnek, mint egy kosárcsapatnak 12 eleme van.

      var Betuk: array['A'..'Z'] of byte;

Tegyük fel, hogy egy szövegben meg szeretnénk határozni az egyes betűk darabszámát. Ekkor az a legegyszerűbb, ha a tömb elemeire magukkal a betűkkel hivatkozhatunk, ezért a tömb indextípusának a karakteres típus egy intervallumát választottuk.

A tömbök egy fontos jellemzője a dimenziójuk. Az eddigi példákban egydimenziós tömböket láttunk, egy sorként lehet őket elképzelni, szemléltetni. Például:


Az egydimenziós tömböknek gyakori elnevezése a vektor.
Képzeljük el, hogy a 30 fős osztálynak nem csak a matek, hanem mind a 10 tantárgy osztályzatát kezelni szeretnénk. Ekkor használhatunk egy kétdimenziós tömböt, melyet egy táblázatként tudunk elképzelni. Az alábbi példa egy 3 X 5-ös tömböt illusztrál.







A kétdimenziós tömböt kétféleképpen is deklarálhatjuk, az első forma azt tükrözi, hogy ez egy olyan tömb, amelynek az elemei is tömbök, mint ahogy a táblázat is sorokból áll.

   var Naplo: array[1..30] of array[1..10] of byte;

Ugyanezen tömbnek egy tömörebb formájú deklarációja:

   var Naplo: array[1..30, 1..10] of byte;

A Naplo tömböt egy 30 sorból és 10 oszlop álló táblázatként képzelhetjük el. A kétdimenziós tömbök gyakori elnevezése a mátrix.

Analóg módon deklarálhatunk és használhatunk három, illetve több dimenziós tömböket is.

2 Típusdefiníció

Ön a tömb adattípus kapcsán a Pascal nyelv egy új elemét is megtanulja, a típusdefiníciót. Eddig a változóink deklarálásakor a nyelv standard, előre definiált típusait (integer, real, array, stb.) használtuk. A Pascal lehetőséget nyújt saját típusok definiálására, amelyeket a változók deklarációjakor felhasználhatunk. A típusok definiálása áttekinthetőbbé teheti programjainkat, másrész helyenként a nyelv szintaktikája is megköveteli (ld. később, tömb paraméterek). A szintaktikája:

type TTantargy = array[1..30] of byte;

var Matek, Magyar, Tesi: TTantargy;

A típusdefiníciót a type kulcsszóval vezetjük be. Példánkban a típus nevét T betűvel kezdtük, ezzel is utaltunk arra, hogy ez nem egy váltózónak az azonosítója. A név után egyenlőségjelet (nem kettőspontot!) követően kell megadnunk a típus leírását, a változó deklarációban megszokott módon. A további példáinkban mi is fogunk élni a típusdefiníció lehetőségével, kérem Ön is használja az ellenőrző feladataiban.
Előfordulhat, hogy egy eljárásnak, vagy függvénynek tömb típusú paramétert szeretnénk adni. Ekkor a formális paraméter típusának megadásakor csak típusazonosítót használhatunk. Például írjunk olyan függvényt, ami egy TTantargy típusú tömbnek kiszámítja az átlagát. A függvény fejének a jó és a szintaktikailag hibás alakja:

{Jó}
function Atlag(T: TTantargy): real;

{Hibás}
function Atlag(T: array[1..30] of byte): real;

3 Indexelés, műveletek, feldolgozása

A tömb összetett adattípus, beolvasni a billentyűzetről, illetve kiírni a képernyőre csak elemenként lehet, persze csak akkor, ha az elemeinek a típusa ezt megengedi.
A tömb egy eleméhez az indexelés segítségével férhetünk hozzá: a tömb azonosítója után kell írnunk szögletes zárójelben az adott elem indexét. Például:

       Matek[5] := 4;

A Matek[5] az 5. tanuló jegyét jelenti, a tömb alaptípusának (byte) megfelelő változóként használható.

       WriteLn('Az A betűk száma: ', Betu['A']);

       ReadLn(Naplo[5, 3]);

Többdimenziós tömbök esetén az egyes dimenziók indexeit vesszővel választhatjuk el egymástól. Példánkban beolvastuk az 5. tanuló 3. jegyét.
Helyes az alábbi forma is:

       ReadLn(Naplo[5][3]);

Egy karakterlánc alaptípusú tömbben hivatkozhatunk a tömbelemek egy-egy karakterére is.

var Nevsor: array[1..30] of string;

Például a Nevsor tömb 5. elemének 3. karaktert:

       Nevsor[5, 3] := 'A';  {vagy}
       Nevsor[5][3] := 'A';

Többek között azért használunk tömböket, hogy programjainkban ne a fenti példáknak megfelelően egyenként, hanem ciklusok segítségével kezeljük az elemeiket. Mivel általában ismerjük a tömb elemeit, ezért a for ciklust használhatjuk a kezelésére úgy, hogy a ciklusváltozó végiglépdel a tömb indextartományán.

Példa:
program Jegyek;
uses Crt;
const Letszam = 5;
type TTantargy = array[1..Letszam] of byte;
var Matek, Magyar, Tesi: TTantargy;

procedure TargyBe(var T: TTantargy);
  var i: integer;
  begin
    for i := 1 to Letszam do
      begin
        Write('Kérem az ', i, '. tanuló jegyét: ');
        ReadLn(T[I]);
      end;
  end;

function TargyAtlag(T: TTantargy): real;
  var i: integer;
      Osszeg: integer;
  begin
    Osszeg := 0;
    for i := 1 to Letszam do
      Osszeg := Osszeg + T[I];
    TargyAtlag := Osszeg / Letszam;
  end;

begin
  ClrScr;
  WriteLn('Írja be a matek jegyeket!');
  TargyBe(Matek);
  WriteLn;
  WriteLn('Írja be a magyar jegyeket!');
  TargyBe(Magyar);
  WriteLn;
  WriteLn('Írja be a tesi jegyeket!');
  TargyBe(Tesi);
  WriteLn;
  WriteLn('A matek jegyek átlaga: ',
            TargyAtlag(Matek):5:2);
  WriteLn('A magyar jegyek átlaga: ',
            TargyAtlag(Magyar):5:2);
  WriteLn('A tesi jegyek átlaga: ',
            TargyAtlag(Tesi):5:2);
  ReadLn
end.

A példában több érdekesség is található. Mivel több tárgy jegyeit is hasonlóan szeretnénk kezelni, ezért célszerű alprogramokat írnunk. A TargyBe eljárás beolvassa egy tantárgy osztályzatait. Mivel az eljárás megváltoztatja a paraméterének az értékét, ezért cím szerinti (var) paraméterátadást kell alkalmaznunk. A TargyAtlag függvény az érték szerinti paraméterként megkapott tömb elemeinek az átlagát számolja ki. Mindkét alprogram a tömbök kezelését for ciklussal végzi. A létszámot, ami egyben a tömbök mérete is, célszerű konstansként definiálnunk, hiszen így változáskor csak egy helyen kell módosítanunk a programunkat.

Példa: Kérjük be egy 3x4-es mátrix elemeit, írjuk ki az elemek átlagát, a sorok összegét és a legnagyobb elemét a mátrixnak!
program Matrix;
uses Crt;
type TMatrix = array[1..3, 1..4]of integer;
var M: TMatrix;
    i, j, Osszeg, Max: integer;

begin
ClrScr;
{A mátrix elemeinek beolvasása}
for i:=1 to 3 do
  for j:=1 to 4 do
    begin
      Write(i,'. sor ',j,'. elem: ');
      ReadLn(M[i,j]);
    end;
WriteLn;
{A mátrix kiírása}
WriteLn('A mátrix: ');
for i:=1 to 3 do
  begin
    for j:=1 to 4 do Write(M[i,j]:3);
    WriteLn;
  end;

{Az átlag kiszámítása}
Osszeg:=0;
for i:=1 to 3 do
  for j:=1 to 4 do
    inc(Osszeg,M[i,j]);
WriteLn;
WriteLn('Átlag: ', Osszeg/12:10:2);

{A sorok összege}
WriteLn;
WriteLn('A sorok összege:');
for i:=1 to 3 do
  begin
    Osszeg:=0;
    for j:=1 to 4 do inc(Osszeg,M[i,j]);
    WriteLn(Osszeg);
end;
{A legnagyobb elem}
Max:=M[1,1];
for i:=1 to 3 do
  for j:=1 to 4 do
    if M[i,j]>Max then
      Max:=M[i,j];
WriteLn;
WriteLn('A legnagyobb elem: ',Max);
ReadLn
end.

Milyen műveleteket végezhetünk a tömbökön? Két azonos típusú tömbre az értékadás és az egyenlőség vizsgálat megengedett.
A következő példában helyes, illetve szintaktikailag hibás értékadásokat láthatunk:

type VektorTip = array[1..4] of integer;
     MatrixTip = array[1..5] of VektorTip;
var  v1, v2: VektorTip;
     m1, m2: MatrixTip;
     v3: array[1..4] of integer;

v1 := v2;     {Helyes}
m1 := m2;     {Helyes}
m1[2] := v1;  {Helyes}
v1 := m1;     {Hibás}
v1 := v3;     {Hibás!!}

4 A tömbökkel kapcsolatos alapfeladatok

Ebben az alfejezetben a tömbökhöz kapcsolódó tipikus programozási feladatokat, algoritmusokat tekintjük át.

Gyűjtés
Hasonló a számláláshoz, de itt a bejövő elemeket valamilyen tulajdonság, szempont szerint szelektáljuk. A tömb elemei a számlálók, amelyek egy-egy különböző tulajdonságnak felelnek meg.
Az általános algoritmus akkor, ha nem tudjuk előre az elemek számát (végjelig történő feldolgozás):

Gyűjtő tömb inicializálása (többnyire lenullázása)
Hozzáférés az első elemhez
Ciklus amíg az Elem <> Végjel
  Elem vizsgálata, Index képzése
  Gyűjtő[Index] módosítása (pl. inkrementálása)
  Hozzáférés a következő elemhez
Ciklus vége

Az Index képzése azt jelenti, hogy meg kell vizsgálnunk, hogy az adott Elem milyen tulajdonságú, azaz melyik Gyűjtő tömb elemhez tartozik (melyik Gyűjtő tömb elemet kell növelni). Ismert elemszám esetén természetesen for ciklust kell alkalmaznunk.

Példa: Olvassunk be osztályzatokat 0 végjelig, és számoljuk meg, hogy hány ötös, négyes, hármas, kettes, illetve egyes volt közöttük.
program JegySzam;
uses Crt;
type TGyujto = array[1..5] of integer;
var  Gyujto: TGyujto;
     Jegy, i: byte;
begin
  ClrScr;
  {A gyűjtő tömb inicializálása}
  for i := 1 to 5 do
   Gyujto[i] := 0;

  {Gyűjtés}
  Write('Osztályzat: '); ReadLn(Jegy);
  while Jegy <> 0 do
    begin
      Inc(Gyujto[Jegy]);
      Write('Osztályzat: '); ReadLn(Jegy);
    end;

  {Kiírás}
  WriteLn;
  for i := 1 to 5 do
    WriteLn(i, ': ',Gyujto[i],' db');
  ReadLn;
end.


Itt az index képzése nagyon egyszerű, hiszen a vizsgált elem, azaz a jegy maga az index.



Rendezés
Nagyon gyakori feladat az, amikor egy tömb elemeit valamilyen szempontból rendezni szeretnénk. Ezen problémára számos algoritmus létezik, mi egyet mutatunk meg, a minimumkiválasztásos rendezést. Ennek a lényege a következő: először megkeressük a tömb legkisebb elemét, majd ezt kicseréljük az első elemmel, ezután a tömb 2. elemétől kezdődő szakaszában keressük meg a legkisebb elemet, majd ezt kicseréljük a 2. elemmel, majd ezt az eljárást folytatjuk a 3., 4. stb. elemmel kezdődő résztömbökre, egészen az utolsó előtti elemel kezdődő résztömbig.
Az algoritmus mondatszerű leírása:

Ciklus I = 1 –től Elemszám – 1 –ig
  {a résztömbben a legkisebb elem indexe}
  MinIndex := I
  Ciklus J := I + 1 –től Elemszám –ig
    Ha Tömb[J] < Tömb[MinIndex] akkor
      MinIndex := J
    Elágazás vége
  Ciklus vége
  Ha I <> MinIndex akkor
    Csere(Tömb[I], Tömb[MinIndex])
  Elágazás vége
Ciklus vége

Példa: Egy 10 elemű, neveket tartalmazó tömböt rendezzünk névsor szerint!
program Rendez;
uses Crt;
type TNevsor = array[1..10] of string;
var  Nevsor: TNevsor;
     i, j, MinIndex: byte;
     S : string;
begin
  ClrScr;
  {A névsor beolvasása}
  for i := 1 to 10 do
    begin
      Write('Az ',i,'. név: ');
      ReadLn(Nevsor[i]);
    end;

  {Rendezés}
  for i := 1 to 9 do
    begin
      MinIndex := i;
      for j := i + 1 to 10 do
        if Nevsor[j] < Nevsor[MinIndex] then
        MinIndex := j;
      if i <> MinIndex then
        begin
          S := Nevsor[i];
          Nevsor[i] := Nevsor[MinIndex];
          Nevsor[MinIndex] := S
        end;
    end;

  {Kiírás}
  WriteLn;
  for i := 1 to 10 do
    WriteLn(Nevsor[i]);
  ReadLn;
end.


Kérem, hogy az [1] irodalomban tanulmányozzon további rendezési algoritmusokat (299-306 old.)!

Keresés
Arra vagyunk kíváncsiak, hogy egy adott elem benne van-e a tömbben és a tömb hányadik eleme (mi az indexe). A keresési algoritmus függ attól, hogy a tömb rendezett-e, illetve, hogy egyszer, vagy többször szerepelhet-e az adott elem a tömbben.
Nézzük a rendezetlen tömbben való keresés algoritmusát úgy, hogy az elemnek csak az első előfordulására vagyunk kíváncsiak. Ekkor a keresett elemet sorra összehasonlítjuk a tömb elemeivel, a keresést addig folytatjuk, amíg meg nem találtuk az elemet, illetve még nem értünk a tömb végére.

Be: Elem
I := 1
Ciklus amíg (I <= Elemszám) és (Elem <> Tömb[I])
  I := I + 1;
Ciklus vége
Ha I <= Elemszám akkor
  Ki: A keresett elem indexe: I
Egyébként
  Ki: Nincs ilyen elem
Elágazás vége

Példa: Keressük meg egy beolvasott nevet a rendezetlen névsor tömbben!
program Keres;
uses Crt;
type TNevsor = array[1..10] of string;
var  Nevsor: TNevsor;
     i : byte;
     Nev : string;
begin
  ClrScr;
  {A névsor beolvasása}
  for i := 1 to 10 do
    begin
     Write('Az ',i,'. név: ');
      ReadLn(Nevsor[i]);
    end;

  WriteLn;
  Write('A keresendő név: ');
  ReadLn(Nev);
  i := 1;
  while (i <= 10) and (Nev <> Nevsor[i]) do
    Inc(i);
if i <= 10 then
    WriteLn('A keresett név indexe: ',i)
  else
    WriteLn('Nincs ilyen név.');

  ReadLn;
end.


Kérem, hogy az [1] irodalomban tanulmányozza a keresés további eseteinek algoritmusait!



1. Válogassa ki azon jellemzőket, amelyek érvényesek a tömb adattípusra!
A. különböző típusúak lehetnek az elemei
B. mérete rögzített
C. az indextípusa csak sorszámozott lehet
D. hozzáférhetünk az egyes elemeihez
E. az elemek sorrendje nem kötött

2. Hány eleme van az alábbi tömbnek?
Var T: array[2..4, 1..5]of byte;

3. Lehet-e négy dimenziós egy tömb?

4. Mi a hiba az alábbi programrészletben?
type TTomb = array['A'.. 'Z'] of byte;
var  Tomb: TTomb;
...
TTomb['M'] := 20;

5. Adott az alábbi deklaráció.
Var  Szavak :Array [1..20] Of String;

Hogyan hivatkozhatunk az 5. szó 7. karakterére?
A. Szavak[7][5];   B. Szavak[5,7];
C. Szavak[5;7];    D. Szavak[5][7];

6. Adottak a következő deklarációk:
Var T1,T2:Array[1..5,1..5] of Real;

Melyek a mind szintaktikusan, mind szemantikusan helyes utasítások:
A. Readln(T1);           B. T1[2,3]:=5.55;
C. T1[2][3]:=5.55;       D. T1[5,5]:=T2[0][6];
E. Writeln(T2[3,3]+'5.55');

7. Egy növekvően rendezett tömbben keressük egy elem első előfordulását. Meddig folytassuk a keresést?

8. Írjon programrészletet, amely a tananyagban példaként szereplő Betuk tömböt használja gyűjtő tömbként egy szóban előforduló betűk megszámlálására:
var Betuk: array['A'..'Z'] of byte;



1. Adott a következő tömbdeklaráció:
Var T:Array[100..200,0..15] of Integer;

Hány elemű  a T tömb:
A. 1500 B. 3000
C. 3200 D. 1616

2. Adottak a következő deklarációk:
Var A:Byte;
    Tomb:Array['a'..'z'] Of Integer;

Melyek a szintaktikailag és szemantikailag helyes értékadó utasítások:
A. Tomb[A]:=3;     B. Tomb['a']:=A;
C. Tomb['a']:=4;   D. Tomb[4]:='a';

3. Adott a következő deklaráció:
Var Tomb:Array[1..10] Of String[20];

A tömb második karakterláncának 5. karakterére szeretnénk hivatkozni. Hogyan helyes?
A. Tomb[2][5]   B. Tomb[5][2]
C. Tomb[2,5]    D. Tomb[5,2]

4. Töltsön fel egy 10 elemű tömböt egészekkel, és írassa ki fordított sorrendben!

5. Olvassa be a novemberi napi hőmérsékleteket (tömb). Listázza ki az adatokat! Írja ki a maximális hőmérsékletet! Írja ki az átlag hőmérsékletet! Írja ki, melyik nap volt a legnagyobb az eltérés az átlagtól!

6. Töltsön fel egy 6*10-es mátrixot véletlen egyjegyű egész számokkal, majd írja ki mátrix alakban!

7. A program dobjon N-et kockával, írja ki az egyes esetek számát, s a dobások átlagát! Használjon gyűjtő tömböt!

8. Írjon programot, amely * végjelig beolvas neveket, maximum 20-at. Tehát egy 20 elemű tömböt kell deklarálnia, amelynek nem biztos, hogy minden elemét használni fogja. Rendezze névsor szerint a neveket, majd írassa ki őket!


Gyakorló feladatok megoldása

1. B, C, D

2. 15, három sora és öt oszlopa van

3. Igen

4. TTomb['M'] := 20; utasítás hibás, mivel TTomb nem egy változó, hanem egy típus.

5. B, D

6. B, C

7. Amíg nem értünk a tömb végére és a keresett elem nagyobb a tömb aktuális eleménél.

8.
for Kar := 'A' to 'Z' do
  Betuk[Kar] := 0;

for i := 1 to Length(Szo) do
  Inc(Betuk[UpCase(Szo[i])]);

A Betuk tömb azon elemét növeljük, melynek indexe a Szo aktuális karakterének a nagybetűs verziója.

Nevezd meg! - Ne add el! - Így add tovább! 2.5 Magyarország
(CC BY-NC-SA 2.5)


LicencTulajdonság
 Nevezd meg!CC BY
 Nevezd meg! - Ne változtasd!CC BY-ND
 Nevezd meg! - Így add tovább!CC BY-SA
 Nevezd meg! - Ne add el!CC BY-NC
 Nevezd meg! - Ne add el! - Így add tovább!CC BY-NC-SA
 Nevezd meg! - Ne add el! - Ne változtasd!CC BY-NC-ND
 Közkincs - Public DomainCC0






Nincsenek megjegyzések:

Megjegyzés küldése