TARTALOMJEGYZÉK 1. Alapfeladatok és algoritmusok 1.1.1. Algoritmus készítés 1.1.1. Az algoritmus fogalma 1.1.2. Algoritmus tervezés, moduláris programozás 1.1.3. Strukturált program, mondatszerű leírás 1.2. Szoftver fejlesztése 2. Összetett adatszerkezetek 2.1. Egydimenziós tömbök 2.2. Stringek 2.3. Többdimenziós tömbök 2.3. A rekord fogalma 3. Programozási tételek 3.1. Egy sorozathoz egy értéket rendelő algoritmusok 3.1.1. Sorozatszámítás 3.1.2. Eldöntés 3.1.3. Kiválasztás 3.1.4. Lineáris keresés 3.1.5. Megszámlálás 3.1.6. Maximum kiválasztás 3.2. Egy sorozathoz egy sorozatot rendelő algoritmusok 3.2.1. Kiválogatás 3.2.2. Másolás 3.2.3. Rendezés 3.3. Több sorozathoz egy sorozatot rendelő algoritmusok 3.3.1. Egyesítés 3.3.2. Metszetképzés 3.3.3. Különbségképzés 3.3.4. Rendezett sorozatok egyesítése
3.4. Egy sorozatból egy sorozatot előállító algoritmusok 3.4.1. Szétválogatás 4. A rekurzió fogalma, rekurzív algoritmusok 5. Rendezési és keresési algoritmusok 5.1. Tömbök rendezése. 5.1.1. Rendezés beszúrással 5.1.2. Rendezés közvetlen kiválasztással 5.1.3. Cserélő rendezések 5.1.4. Cserélő rendezések - Buborék rendezés 5.1.5. Gyorsrendezés (Quicksort) 5.2. Keresési eljárások 6. Adatszerkezetek 6.1. Lineáris listák 6.1.1. Szekvenciális helyfoglalás 6.1.2. Láncolt helyfoglalás, láncolt sor 6.1.3. Ciklikusan láncolt listák 6.1.3. Kétszeresen láncolt listák 6.2. Hasító táblázatok 6.3. Fák, bináris fák 7. Függvények általános jellemzői 8. Adatállományok kezelése 9. Gráfalgoritmusok 9.1. Alapfogalmak, jelölések 9.2. A legrövidebb utak problémája (egy forrásból) 9.3. Az összes csúcspár közötti távolság meghatározása 9.4. Mélységi bejárás 9.5 A szélességi bejárás 9.6 Maximális párosítás páros gráfokban 10. Turing-gépek. Az NP nyelvosztály 2
1. Alapfeladatok és algoritmusok 1.1. Algoritmus készítés Áttekintés: Az algoritmus fogalma, tulajdonságai Az algoritmus tervezés lépései, moduláris programozás A strukturált program fogalma, elemei Algoritmus leíró eszközök, a pszeudokód 1.1.1. Az algoritmus fogalma Feladat: homokvár körüli vizesárok feltöltése tengervízzel. Megoldás: Vedd a kezedbe a kisvödröt. Ismételd az alábbiakat mindaddig, amíg tele nem lesz a vizesárok: Merítsd tele a vödröt tengervízzel. Töltsd a vizesárokba. Tedd le a kisvödröt. Bár a fenti tevékenységek ismétlést tartalmaznak, véges számú lépésben a feladat megoldásához (megtelik a vizesárok) vezetnek. Algoritmusnak nevezzük az egyértelműen előírt módon és sorrendben végrehajtandó tevékenységek véges sorozatát, melyek alapján egy feladattípus véges számú lépésben megoldható. Az algoritmus leírásának egyértelműnek, pontosnak, lépésenként végrehajthatónak kell lennie. FONTOS, az algoritmus NYELVFÜGGETLEN, vagyis nem tartalmaz olyan jelöléseket, elemeket, ami csak egy, vagy néhány programozási nyelvben fordul elő! Az algoritmus struktúráját szekvenciák, szelekciók és iterációk alkotják, melyeket tetszőleges mélységben egymásba lehet ágyazni. A legtöbb számítógépben csak egy processzor van, amely képtelen a párhuzamos feldolgozásra - mi sem foglalkozunk ilyen algoritmusokkal. Az algoritmus alapján elkészítendő programjainkban a számítógép egyszerre csak egy tevékenységet képes végrehajtani, majd ezután kezd hozzá a következő tevékenységhez, vagy megvizsgál egy feltételt és annak függvényében az egyik, vagy egy másik tevékenység végrehajtását kezdi el. Mi a C++ nyelvet használjuk majd a feladataink végső megoldásához. Egy számítógép által érthető nyelven megírt algoritmust programnak nevezünk. Az algoritmus, illetve program leglényegesebb tulajdonságai, és a vele szemben támasztott követelmények: Az algoritmus lépésekből (elemi tevékenységekből, instrukciókból, utasításokból) áll. Az algoritmus végrehajtása lépésenként történik. A végrehajtás során megtett lépések sorozatát folyamatnak (processznek ) nevezzük. Minden lépésnek egyértelműen végrehajthatónak kell lennie. Az algoritmus leírásában a végrehajtót minden lehetséges esetre fel kell készíteni. A végrehajtónak minden egyes lépés után tudnia kell, hogy mi lesz a következő lépés. Egy algoritmusban hivatkozhatunk összetett lépésekre is, melynek részletezését később adjuk meg. A részletezés során olyan szintig kell eljutnunk, ahol már végrehajtható (elemi) tevékenységek szerepelnek. A végrehajtásnak mindig van valamilyen tárgya. Ezeket a tárgyakat a programozásban adatoknak nevezzük. Az adatoknak tulajdonságai vannak. Az algoritmus készítésénél csak azokat az adatokat és tulajdonságokat vesszük figyelembe, amelyek a feladat végrehajtásához szükségesek. Ezt a válogatási, egyszerűsítési eljárást absztrakciónak nevezzük. A végrehajtandó utasításoknak valamilyen célja van, a végrehajtás során megváltoznak az adatok bizonyos tulajdonságai. Az algoritmusnak véges számú lépésben el kell vezetnie a feladat megoldásához. A végesség a meghatározásban kétszer is szerepel. A feladat megoldására szolgáló lépések száma is vége kell legyen, de minden egyes lépésnek is be kell fejeződnie. Az algoritmusnak általában vannak bemenő (input) adatai, melyeket felhasznál. Ezeket az adatokat nevezzük inputnak. Az input adatok bizonyos jól meghatározott halmazból kerülnek ki. Az algoritmusnak legalább egy kimenő (output) adatot produkálnia kell. Bármilyen fantasztikus dolgokat is művel egy folyamat, ha nem kommunikál a külvilággal, definíció szerint nem algoritmus. (Az output természetesen nem feltétlenül numerikus érték, lehet szöveg, grafika, vagy más egyéb információ, amely az algoritmus eredményeként jön létre.) 3
Az algoritmus legyen hatékony. Az instrukciók legyenek könnyen követhetőek, pontosak, egyszerűen végrehajthatóak. Legyen a program felhasználóbarát, a felhasználó szempontjait maximálisan vegye figyelembe. 1.1.2. Algoritmus tervezés, moduláris programozás Az algoritmus tervezésének első lépése a feladat körültekintő és alapos elemzése, a probléma megfogalmazása. A feladat leírását feladat specifikációnak is szokás nevezni, ezt írásban is rögzíteni kell. Az algoritmusok leírására számtalan eszköz létezik, ilyen a mondatszerű leírás, a folyamatábra, a stuktogram vagy a Jackson jelölés. Feladat specifikáció Egy számítógépen megoldandó feladat felmerülésekor törekedni kell arra, hogy a feladat megfogalmazása egyértelmű legyen, valamennyi lehetséges esetet lefedjen. A megrendelő gyakran hiányos, és/vagy pontatlan igényekkel lép fel, a megoldási módszer keresése viszont csak akkor kezdődhet el, ha a feladatot pontosan ismerjük és írásban is rögzítettük, vagyis megadtuk a feladat specifikációt. Adatok tervezése Mivel az algoritmus adatokon dolgozik, meg kell tervezni a feladatban szereplő külső és belső adatokat, adat szerkezeteket. Csak ezután kezdhetünk hozzá az adatokat mozgató, manipuláló lépések megtervezéséhez. Moduláris programozás Az algoritmus (program) tervezésének alapja a dekompozíció. A bonyolultabb feladatokat általában nem lehet egyszerre megoldani, ezért részekre, modulokra kell azokat bontani. A részekre ki kell dolgozni a megoldás menetét, majd a részeket újra össze kell állítani, hogy azok együttműködve, egymásra épülve a teljes feladat megoldását szolgáltassák. A moduláris programozás olyan programozási mód, melyben a teljes program modulokból áll. Az egyes modulok jól meghatározott részfeladat megoldását végzik, kezelhető méretűek, egyértelmű céljuk van és jól definiáltan csatlakoznak a program többi moduljához. A moduláris programozás irányelvei a következők: "Oszd meg és uralkodj" elv: A feladatokat egyértelműen le kell osztani modulokra. A modulok belső működésébe más modul nem szól bele. A modul a saját feladatának tökéletes elvégzéséért felelős. Fontos, hogy a modulok lehetőleg egymástól függetlenül működjenek, mert így a hibák kiszűrése egyszerűbb, a feladat átláthatóbb, könnyebben módosítható. Adatok (információ) elrejtésének elve: Az egyes modulok csak saját adataikon dolgozzanak, csak akkor használjanak közös adatokat, ha ez elkerülhetetlen. Döntések elhalasztásának elve: Csak akkor hozzunk meg egy döntést, ha az elengedhetetlenül szükséges. Mindazon döntéseket, amelyekhez még nincs elég ismeretünk, halasszuk későbbre - különben előfordulhat, hogy egy korán meghozott döntést a későbbiekben meg kell változtatnunk. Döntések kimondásának elve: A feladat megoldása során, ha már meghoztunk egy döntést, azt le kell rögzíteni, mert különben erről később esetleg megfeledkezve, ellentmondásos döntéseket hozhatunk. A modulokra bontás iránya alapvetően kétféle lehet: Felülről lefelé (top-down) tervezés esetén a megoldást felülről lefelé, fokozatosan, lépésenként finomítjuk. A feladatot részfeladatokra, a részfeladatokat ismét kisebb feladatokra bontjuk mindaddig, amíg az úgynevezett elemi tevékenységekhez jutunk. Az elemi tevékenységek tovább nem bonthatók, egy lépésben végrehajthatók. Az alulról felfelé (bottom-up) tervezés lényege, hogy már meglévő, kész modulokból építkezünk. Erre akkor kerül sor, ha bizonyos részfeladatokat egy előző feladat kapcsán már megoldottunk (gondolva a későbbi felhasználásokra is), vagy amikor egy rutingyűjteményt (szoftvert) vásároltunk. A bonyolult feladatok megoldásánál többnyire mind a felülről lefelé, mind az alulról felfelé tervezést használjuk, megfelelően ötvözve a két módszert. 1.1.3. Strukturált program, mondatszerű leírás Tetszőleges algoritmus felépíthető a következő elemekből: Szekvencia: Egymás után végrehajtandó tevékenységek sorozata. Szelekció: Választás megadott tevékenységek közül Iteráció: Megadott tevékenységek ismételt végrehajtása. Feltétel nélküli ugrás: Vezérlés átadása a program egy megadott pontjára. Tulajdonképpen a feltétel nélküli ugrás nem igazán szükséges elem, gyakori használata áttekinthetetlenné teszi a programot. 4
Böhm és Jacopini tétele A szekvencia, szelekció és az iteráció segítségével minden olyan algoritmus felépíthető, amelynek egy belépési és egy kilépési pontja van. Strukturált program A csak szekvenciákból, szelekciókból és iterációkból építkező programot strukturált programnak nevezzük. A strukturált programozásban nem megengedett a feltétel nélküli ugrás, így többek között a ciklusból sem ugorhatunk ki. Ebből az is következik, hogy a program minden vezérlő szerkezetének (szekvencia, szelekció, iteráció) - és magának a teljes programnak is - egyetlen belépési és egyetlen kilépési pontja van, így a program lényegesen áttekinthetőbb. A strukturált programozás általános módszereit E. W. Dijkstra dolgozta ki. Mondatszerű leírás A mondatszerű leírás, vagy pszeudokód lényege, hogy az algoritmust mondatszerű elemekből építjük fel. Ez nagyon hasonlít a beszélt nyelvre, illetve annak írásos formájára, de be kell tartanunk bizonyos szabályokat, a struktúrák képzésére megállapodás szerinti formákat és szavakat használunk. Az alapelemek az alábbiak: bevitel, kivitel, szekvencia, szelekciók, ciklusok Bevitel, kivitel: Be:... változók felsorolása...[...] az adatokkal szemben támasztott követelmények Ki:... kifejezések felsorolása... [...] a kiírás formájára vonatkozó követelmények Szekvencia: Az egymás után végrehajtandó tevékenységek sorozata. Tevékenység1 Tevékenység2 Tevékenység3 Az egy sorba írt tevékenységeket kettőspont választja el: Tevékenység1 : Tevékenység2 : Tevékenység3 PÉLDA: Elindulás autóval... Értékadó utasítás: a szekvencia egy speciális fajtája, formája: változó = kifejezés Szelekciók (elágazások, feltételes utasítások): Programrészek közötti választás Egyágú szelekció Ha Feltétel akkor Utasítás(ok) Elágazás vége Jelentése: Ha Feltétel teljesül, akkor az Utasítás(ok) végrehajtásra kerülnek, egyébként nem. A program az Elágazás vége után folytatódik. Kétágú szelekció Ha Feltétel akkor Utasítás(ok)1 Egyébként Utasítás(ok)2 Elágazás vége 5
Jelentése: Ha Feltétel teljesül, akkor az Utasítás(ok)1 kerül(nek) végrehajtásra, egyébként Utasítás(ok)2. A program mindkét esetben az Elágazás vége után folytatódik. PÉLDA: Ha süt a nap, akkor kimegyek napozni egyébként kitakarítom a szobámat. Elágazás vége Szelekció IGAZ és HAMIS ágán is állhat újabb szelekció. Ezeket egymásba ágyazott szelekcióknak nevezzük. Az ilyen egymásba ágyazott szelekciók speciális esete a többágú szelekció. Az egy/kétágú szelekció valamilyen (logikai)feltételtől függő tevékenység végrehajtás, míg a többágú szelekció egy kifejezés (egész) értékétől függő tevékenység végrehajtás. Minden többágú szelekció megoldható egymásba ágyazott kétágú szelekcióval. Többágú szelekció Elágazás kifejezés szerint értékcsoport1 esetén Utasítás(ok)1 értékcsoport2 esetén Utasítás(ok)2... értékcsoportn esetén Utasítás(ok)n egyéb esetben Utasítás(ok)n+1 Elágazás vége Az egyes értékcsoportok véges, diszkrét halmazokat alkotnak! Egy értékcsoportot, az összes elemének felsorolásával lehet megadni. Jelentése: Ha a kifejezés értéke valamelyik értékcsoport tartományába esik, akkor a neki megfelelő utasítás(oka)t kell végrehajtani, majd a feladat megoldása az Elágazás vége után folytatódik. PÉLDA: Elágazás Piros lámpa esetén Megállok : Kézifék beh. Sárga lámpa esetén Megállok : Kézifék beh Zöld lámpa esetén Továbbhaladok Elágazás vége Ha egymásba ágyazott szelekciók esetén pl. mindkét szelekció igaz ágán ugyanazt a tevékenységsorozatot kell végrehajtani, akkor a feltétel megfogalmazható összetett logikai kifejezésként és csak egy szelekcióra van szükség, a már megbeszélt logikai operátorok segítségével. Iteráció (Ciklus): Valamely tevékenység sorozat ismételt végrehajtását jelenti. Az ismétlés feltételhez kötött. Példa: számolj el 50-ig 1. Gondolj az 1-es számra, takard el a szemedet 2. Mondd ki hangosan a gondolt számot 3. Növeld meg eggyel a gondolt számot 4. A gondolt szám nagyobb, mint 50? Ha nem, akkor folytasd a 2-es sorszámú feladattól. 5. Miután kimondtad hangosan az 50-et is, indulj megkeresni a társaidat. A programozási nyelvekben bizonyos utasítások ismétlését biztosító programszerkezeteket iterációnak vagy ciklusnak nevezzük. Az ismétlés mindaddig tart, amíg az ismétlési, vagy lefutási feltétel igaz. Vannak olyan programszerkezetek is, ahol a ciklus akkor fejeződik be, amikor egy meghatározott kilépési feltétel válik igazzá, vagyis az ismétlés addig tart, amíg a kilépési feltétel hamis. A C nyelvben valamennyi ciklus-utasításban lefutási feltételt kell megfogalmazni. A fenti kis feladat megoldása pszeudokóddal is leírható: Program Változók: gondolt_szam: pozitív egész szám gondolt_szam = 1 Ciklus 6
Ki: gondolt_szam // Ciklusmag utasításai gondolt_szam = gondolt_szam + 1 amíg gondolt_szam <= 50 Ciklus vége Ki: "Aki bújt, aki nem, megyek!" Program vége A ciklusok két típusát különböztetjük meg: - Elöltesztelő ciklus - Hátultesztelő ciklus Elől tesztelő ciklus Ciklus amíg Lefutási feltétel Ciklusmag utasításai Ciklus vége Működése A ciklusmag utasításait mindaddig kell végrehajtani. amíg a Lefutási feltétel igaz. Ha a lefutási feltétel hamissá válik, a végrehajtás a ciklus vége után folytatódik. Ha a lefutási feltétel már a ciklusba való belépéskor hamis a ciklusmag utasításai egyszer sem hajtódnak végre. Tehát a ciklusfeltétel kiértékelése a ciklusmag előtt történik meg, és lehet olyan eset, mikor a ciklusmag egyszer sem fut le, PÉLDA: Ciklus amíg van nálam "Mosópor reklám anyag" elmegyek a köv. lakásig bedobom a csomagot Ciklus vége Az előreolvasás fogalma: Az elöltesztelő ciklus megvalósítása mindig előreolvasással történik. Ez azt jelenti, hogy a feltételben vizsgált adatot előbb meg kell határozni (értékadás vagy beolvasás a billentyűzetről), és azután megvizsgáljuk, hogy a megadott érték megfelel-e a lefutási feltételnek, beléphetünk-e a ciklusmagba. A ciklusmagban pedig biztosítanunk kell a feltétel hamissá válását, különben ha nem változik meg a feltétel, mindig igaz lesz, sohasem lépünk ki a ciklusból, végtelen ciklust kapunk. A biztosíték a feltétel hamissá válásához vagy a megadott érték növelése / csökkentése, vagy újbóli beolvasása, ami a ciklusmag utolsó utasítása kell legyen, így a feltétel kiértékelése szempontjából ismét van egy előre meghatározott érték! Hátul tesztelő ciklus: Ciklus Ciklusmag utasításai mígnem Kilépési feltétel Ciklus vége Működése: A ciklusmag utasításait mindaddig kell végrehajtani. amíg végre a Kilépési feltétel igazzá válik. Ha a Kilépési feltétel igazzá válik, a végrehajtás a ciklus vége után folytatódik. A ciklusmag utasításait egyszer mindenképpen végrehajtja a rendszer, mivel a Kilépési feltétel kiértékelése a ciklusmag utasításainak végrehajtása után történik. PÉLDA Ciklus leszedek egy almát a fáról mígnem tele a kosár Ciklus vége A legtöbb magasszintű programnyelvtől eltérően a C nyelvben a hátul tesztelő ciklus megvalósítása Lefutási feltétel megfogalmazásával, az alábbi pszeudokód szerint történik: Hátul tesztelő ciklus a C-ben 7
Ciklus Ciklusmag utasításai amíg Lefutási feltétel Ciklus vége Működése: A ciklusmag utasításait mindaddig kell végrehajtani. amíg a Lefutási feltétel igaz. Ha a lefutási feltétel hamissá válik, a végrehajtás a ciklus vége után folytatódik. A ciklusmag utasításait egyszer mindenképpen végrehajtja a rendszer, mivel a lefutási feltétel kiértékelése a ciklusmag utasításainak végrehajtása után történik. A lefutási feltétel és a kiugrási feltétel kapcsolata. Gyakran előfordul, hogy a feladat szövegében a feltétel úgy van megfogalmazva, hogy mikor kell az ismétlődő eseményeket befejezni (kiugrási feltétel). Pl. a testmagasság 110 210 cm jelentése: jó eset, ha a testmagasság 110 és 210 cm között van, rossz eset, ha 110 cm alatti vagy 210 cm-nél nagyobb. Mivel a ciklusunk feltétele lefutási feltétel kell legyen, vagyis mindig ciklusban bennemaradási feltételt kell megfogalmaznunk, ezért valamilyen módon a megadott kiugrási feltételt lefutási feltétellé kell konvertálni! A megoldás a DeMorgan azonosságok alkalmazása, az összetett logikai kifejezés negációjával: I, nem(a vagy B) = nem A és nem B II, nem(a és B) = nem A vagy nem B Példák: (logikai feltételek értéke IGAZ és HAMIS illetve C-ben NEM0 és 0) Állítás (jó eset) Tagadás (rossz eset) megvalósítás C-ben a<>0 a=0 a = = 0 a=0 a<>0 a!=0 a>0 a<=0 a<=0 a=0 és b=0 a<>0 vagy b<>0 a!=0 b!=0 a>0 vagy b<=0 a<=0 és b>0 a<=0 && b>0 a>5 és a<10 a<=5 vagy a>=10 a<=5 a>=10 a<5 vagy a>10 a>=5 és a<=10 a>=5 && a<=10 Az elöltesztelő ciklus speciális esete, a Növekményes (számláló) ciklus. Ennél a szerkezetnél nem a feltétel teljesüléséig tart a ciklus, hanem pontosan ismerjük a lefutások számát. Minden Növekményes ciklus megoldható elöltesztelős ciklussal de fordítva ez már nem igaz! Növekményes (számláló) ciklus Ciklus ciklusváltozó =...kezdőértéktől... végértékig... lépésközzel Ciklusmag utasításai Ciklus vége Működése: A ciklusmag utasításai addig és annyiszor hajtódnak végre, míg a ciklusváltozó a kezdőértéktől a végértékéig a lépésköznyi növekedéssel vagy csökkenéssel el nem jut. FELADATOK 1.1. feladat: Készítsen programot, amely bekéri egy felnőtt testmagasság (110-210 cm) és testsúly (30-150 kg) adatait, majd kiírja azokat. Csak a megadott határok közötti értékeket fogadhat el. Megoldás: Pontosítsuk a feladatot: Ha nem a megadott határok közötti értéket kaptam, akkor álljon le a program? Válasz: Nem! 8
Pszeudokód: Elemezzük a feladatot: Külön ciklussal fogjuk bekérni a magasságot, külön a testsúlyt. Így egyszerűbb a lefutási feltételek megfogalmazása és csak a hibásan megadott adat újra bevitelét kérjük. A "bekérési" ciklus addig kell fusson, amíg hibás a kapott érték! Program Változók: magassag, suly (pozitív valós értékek) Ciklus Be: magasság [110-210 cm] amíg (magassag < 110 vagy magassag > 210) Ciklus vége Ciklus Be: suly [30-150 kg] amíg (suly < 30 vagy suly > 150) Ciklus vége Ki: magassag, suly Program vége A hátultesztelő ciklusnál a ciklusmag utasításait egyszer mindenképpen végrehajtja a rendszer. Ez nem mindig a legalkalmasabb szerkezet a feladataink megoldására. 1.2. feladat Állítsunk elő 0 és 50 közötti véletlen-számokat addig, amíg a 25-ös szám generálása is megtörtént. Írassuk ki a kapott számokat. Megoldás: Elemezzük a feladatot: Állítsunk elő egy véletlen-számot, majd ismételjük az alábbi tevékenységeket: Ha a szám nem egyenlő 25-tel, akkor: írjuk ki állítsunk elő egy újabb véletlen-számot. Pszeudokód: Program Változók: szam (pozitív egész) véletlenszám-generátor inicializálása szam = random (51) Ciklus amíg (szam <> 25) Ki: szam szam = random (51) Ciklus vége Ki : "Megvan a 25-ös!" Program vége //előreolvasás //feldolgozás //előreolvasás Példák: Lépetéses ciklus: 9
n-ig az összes prímszám kiírása (1-n-ig pontosan ismert a lefutások száma) 50 véletlen szám meghatározása ( 50-szer kell a ciklusmagot végrehajtani) Elöltesztelős ciklus az első n db prímszám kiírása (lefutási feltétel: db<n) véletlenszám generálása, amíg a 25-öt nem generáltuk (lefutási feltétel: generált szám<>25) csillag végjelig karakterek beolvasása (lefutási feltétel: karakter<> * ) 1.3. feladat Készítsen programot, amely n darab * karaktert ír ki a képernyőre. Megoldás: Pontosítsuk a feladatot: Az n értékét honnét veszem? Pszeudokód: Program Változók: n - darabszám, pozitív egész i - ciklusváltozó, pozitív egész Ciklus Be: n amíg (n<= 0) ciklus vége Ciklus i= 1 kezdőértéktől n végértékig 1 lépésközzel Ki: "*" ciklus vége Program vége 1.4. feladat Készítsen szorzótáblát! Megoldás: Elemezzük a feladatot: A képernyőre először kiírjuk a SZORZÓTÁBLA feliratot középre, majd egy külön sorban a számokat 1- től 9-ig. (szorzó1). Ezután sorra vesszük a másik szorzótényezőt (1-től 9-ig), nevezzük ezt szorzó2-nek, és soronként kiírjuk: a következő szorzót, és a szorzatokat egymás mellé. Két ciklust kell szerveznünk. A képernyőre úgy tudunk a legegyszerűbben egy táblázatot kiírni, hogy vesszük a táblázat sorait egymás után (ez lesz a külső ciklus, szorzo2-vel), és a soron belül vesszük az oszlopokat egymás után (ez lesz a belső ciklus szorzo1-gyel). 1 2 3 4 5 6 7 8 9 1 1 2 3 4 5 6 7 8 9 2 2 4 6 3 4 5 6 7 8 9 Pszeudokód: 10
Program Változók : szorzo1, szorzo2: pozitív egész Ki: "SZORZÓTÁBLA" //Következik a fejléc-sor kiírása Ciklus szorzo1 = 1 kezdőértéktől szorzó1 = 9 végértékig 1-es lépésközzel Ki: szorzo1 Ciklus vége //szorzo1 // a szorzótábla elemei Ki:soremelés Ciklus szorzo2 = 1 kezdőértéktől szorzó2 = 9 végértékig 1-es lépésközzel Ki: szorzo2 //első oszlop elemei Ciklus szorzo1 = 1 kezdőértéktől szorzó1 = 9 végértékig 1-es lépésközzel Ki: szorzo1 * szorzo2 Ciklus vége //szorzo1 Ciklus vége //szorzo2 Program vége 1.5. feladat Keresse meg programmal a 2001 szám legnagyobb természetes osztóját! (1 és önmagán kívül) Megoldás: Elemezzük a feladatot: Egy egész szám osztója legalább kétszer megvan a számban, tehát a legnagyobb osztó keresését elegendő a szám felénél kezdeni, majd egyesével haladni lefelé, amíg az első olyan számot megtaláljuk, amellyel osztva 2001-et, a maradék = 0- Pszeudokód: Program Változók : oszto pozitív egész Ciklus oszto = 2001/2 kezdőértéktől 2 végértékig -1-es lépésközzel Ha (2001 mod oszto = 0) Ki: "A legnagyobb természetes osztó:", oszto kiugrás ciklus vége Ki: "A vizsgálat befejeződött." Program vége Struktúrált megoldás: Program Változók : oszto pozitív egész oszto = 2001/2 Ciklus amíg 2001 mod oszto <>0 és oszto>1 oszto=oszto-1 ciklus vége Ha oszto>1 Ki: "A legnagyobb természetes osztó:", oszto Program vége if (2001%oszto == 0) Összefoglaló 11
Ebben a fejezetben megismertük az algoritmus fogalmát, A moduláris programozás, a strukturált program elemeit. Az algoritmusok leírására itt és a továbbiakban is a pszeudokódot, vagy mondatszerű leírást alkalmazzuk. Az algoritmus alapvető struktúráit (szekvencia, szelekció, iteráció) a további fejezetekben példákon keresztül is begyakoroljuk. Vegyük észre, hogy a pszeudokódnak áttekinthetőnek kell lennie, a bekezdésekkel, a kulcsszavak kiemelésével is hangsúlyozzuk az összetartozó részeket. Az algoritmus, illetve a program készítésénél a logikusan átgondolt, tiszta és követhető megoldásokra kell törekedni. Ellenőrző kérdések: Melyik az a három vezérlőszerkezet, amelyekből a programok felépíthetők? Mi a lényege a "felülről-lefelé", illetve az "alulról felfelé" építkezésnek? Milyen eszközök állnak rendelkezésre az algoritmusok megtervezéséhez? Mit jelentenek az alábbi fogalmak? - Algoritmus - Szekvencia, szelekció, iteráció - Dekompozíció - Lépésenkénti finomítás - Moduláris program - Strukturált program - Pszeudokód A moduláris programozás irányelvei Az egyágú szelekció leírása pszeudokóddal, működése. A kétágú szelekció leírása pszeudokóddal, működése. A többágú szelekció leírása pszeudokóddal, működése. Az elől tesztelő ciklus leírása pszeudokóddal, működése. A hátul tesztelő ciklus leírása pszeudokóddal, működése. A növekményes (számláló) ciklus leírása pszeudokóddal, működése. 1.2. Szoftver fejlesztése Áttekintés A szoftver fejlesztésének fázisai: Analízis, Tervezés, Kódolás, Tesztelés, Dokumentálás A szoftver élete. Egy összetett feladat számítógép segítségével történő megoldása hosszú és bonyolult munkafolyamat eredménye. Bár a számítástechnikában alkalmazott eszközök rohamos fejlődésével e munkafolyamat nagy része automatizálható, a feladat minden részletre kiterjedő megfogalmazása, elemzése, gépre vitele, majd a működő program ellenőrzése és átadása a felhasználó számára mind-mind olyan szervezői, programozói és alkalmazói feladatokat feltételez, amelyek emberi beavatkozást, tehát hibalehetőséget is feltételeznek. Egy program, illetve programcsomag fejlesztése alapvetően négy fázisból áll, melyet kiegészít az elkészült termék dokumentálása: Analízis, Tervezés, Kódolás, Tesztelés, Dokumentálás. Az analízis és a tervezés a probléma megoldására irányul, a kódolás a már átgondolt és írásban is rögzített megoldást implementálja (megvalósítja), azaz lefordítja a számítógép nyelvére. A tesztelés és a dokumentálás a hibátlan és a feladat kiírást maradéktalanul kielégítő termék kibocsátását és a használhatóság biztosítását szolgálja. 1.2.1. Analízis Az analízis (elemzés) során felmérjük, hogy a feladat kiírás kellően pontos-e, egyáltalán megoldható-e a probléma számítógéppel. Tisztázni kell az input adatok körét, a feldolgozás célját és az input adatokat. Meg kell becsülni a feladat megoldásához szükséges időt és a költségeket. A megoldásnak már ebben a szakaszában tisztázni kell minden lehetséges részletkérdést. A feladat megfogalmazása legyen - egyértelmű, - teljes, minden részletre kiterjedő, 12
- érthető, áttekinthető, - pontos, szabatos, - tömör, lényegretörő, - szemléletes, - jól felépített és tagolt, - előretekintő. A beviteli (input) adatokkal szemben a következő kérdéseket kell tisztázni: - Melyek az input adatok (felsorolás, egyértelmű azonosítás), - Az input adatok értéktartománya, beviteli formátumuk, - Milyen módon kell befejezni a bevitelt, - A hibásan bevitt adatok utólagosan javíthatók-e, ha igen, milyen módon, - Vannak-e speciális megszorítások, előírások, - Milyen kapcsolatok vannak a beviteli adatok között, ezeket kell-e ellenőrizni a bevitel során. A kiviteli (output) adatoknál a következőket kell tisztázni: - Mely adatokat kell megjeleníteni, - A megjelenítés milyen formában történjen, - Mely adatokat kell megőrizni későbbi felhasználásra, illetve többszöri megjelenítésre, - Milyen formában kell az adatokat megőrizni. Az analízis eredményeként születik meg a feladat specifikáció, melyet dokumentálni kell. Ennek tartalmaznia kell a feladat pontos megfogalmazásán túl az esetleges képernyő- és listaterveket is. Nagyobb rendszerek esetén rendszertervet készítenek, ez többnyire egy szakértői csapat munkájának ez eredménye. 1.2.2. Tervezés A tervezés feladata, hogy az analízis során összegyűjtött információt alapul véve véglegesen kialakítsa az adat struktúrákat és az adatokat feldolgozó, átalakító algoritmusokat. Egy program megtervezése bonyolult feladat, bár vannak feladat-típusok, többnyire minden feladatot másképpen kell algoritmizálni és nehéz erre általános szabályokat megfogalmazni. Vannak programtervezési módszerek, ajánlások, szabványok, jelölési rendszerek, melyeket ajánlott, illetve egy adott csapatban dolgozva kötelező betartani. Hogy milyen tervezési módszert válasszunk, az a következő dolgoktól függhet: - A számítástechnika által adott lehetőségek és eszközök - Milyen számítógép(ek)re, milyen rendszerre készül a program - A megoldandó feladat nagyságrendje, a kezelendő adatok mennyisége - Milyen módszerek állnak a rendelkezésre - Milyen a tervező csapat felkészültsége, informáltsága - Milyen szoftverek állnak a rendelkezésre a tervezéshez - Vannak-e tradíciók - Van-e megfelelő anyagi fedezet - A fejlesztő cég elvárásai, szabványai, főnöki utasítás. A programtervezési módszereket illetően hosszú ideig a strukturált programozás, programtervezés volt a jellemző. Lényege, hogy a programot felülről lefelé, funkcionálisan egyre kisebb lépésekre (modulokra, eljárásokra) bontjuk. A Jackson-féle programtervezési módszer szintén strukturált módszer, de itt a programszerkezet a bemenő és a kimenő adatszerkezetek összefésüléséből adódik. Az objektumorientált programozás napjaink divatos irányzata, segítségével kisebb energia befektetéssel sokkal hatékonyabban, biztonságosabban tudjuk elkészíteni a programokat. Ennél a módszernél az információ elrejtésének elve minden eddiginél jobban érvényesül: az egyes objektumok adataikkal és eljárásaikkal együtt teljes mértékben felelősek a rájuk bízott feladatok hibátlan megoldásáért. A tervezési szakasz dokumentációja a programterv. A programtervet is tesztelni, ellenőrizni kell, ilyenkor elképzeljük a bemenő adatokat és "fejben" lefuttatjuk a programot. Közben minduntalan fel kell tenni a "Mi történik, ha..." kezdetű kérdéseket. A program megtervezésével lényegében meg van oldva a probléma. Fontos, hogy a programterv hibátlan legyen, mert a későbbi szakaszokban egyre nehezebb a hibajavítás. 1.2.3. Kódolás Ha a terv elkészült, akkor jöhet annak implementálása, azaz kivitelezése. Ennek első fázisa a programtervnek megfelelő forrásprogram elkészítése egy kiválasztott programnyelven. Ez egy eléggé mechanikus folyamat, de feltételezi a programterv alapos ismeretét és természetesen a használt programozási nyelv készségszintű alkalmazását. Mi már elkezdtük a C++ alapvető elemeinek a megismerését, s a továbbiakban is ezt az eszközt használjuk algoritmusaink kódolására. A kódolási szakasz dokumentációja forrásprogram, illetve forrásnyelvi lista. A forrástervi lista akkor jó, ha - pontosan követi a programterv utasításait - áttekinthető, 13
- tömör és egyértelmű megjegyzésekkel van megtűzdelve. A forrásprogram begépelésével természetesen még nincs kész a kódolás, formailag és tartalmilag ellenőrizni kell az elkészült forrásprogramot. A program szintaktikai (formai) és szemantikai (tartalmi) hibákat tartalmazhat. Szintaktikai hibának nevezzük azt a hibát, amely abból eredt, hogy a nyelv formai szabályainak egyikét nem tartottuk be, ezért a fordító számára nem egyértelmű, hogy mit kell tennie. A szemantikai hiba akkor lép fel, ha a program tartalmilag nem azt valósítja meg, amit a programterv előírt. A szintaktikai hibák kiszűréséhez a fordató ad segítséget, a szemantikai hibák felderítése a programozó, illetve az őt segítő tesztelő munkatárs feladata. 1.2.4. Tesztelés Amikor egy program szintaktikailag hibátlan, még nem feltétlenül működik helyesen. Az igényes programozó maga is teszteli, ellenőrzi a programját különböző próba-adatokkal, hogy meggyőződjön a program hibátlan, a programtervnek megfelelő működéséről. A programok tesztelésének külön tudománya van, bonyolultabb feladatoknál alaposan elő kell készíteni a tesztelés fázisait, a teszt-adatokat, hogy lehetőleg minden lehetséges variációt kipróbáljunk. A program tesztelésekor a következőkre kell figyelni: - Pontosan úgy működik-e a program, ahogyan az a feladat leírásában, illetve a programtervben szerepel? - Van-e olyan bemenő adat-kombináció, amelynél hibásan működik, vagy leáll a programfutás? - Eléggé hatékony-e a programfutás? - Biztonságos és egyértelmű-e a program használata? - A program felhasználóbarát? (Pl szépen néz ki a képernyő, nem idegesítő, teljes mértékben szolgálja a felhasználót,...) Statikus tesztelési módszerek: Kódellenőrzés A kódellenőrzés a program szövegének vizsgálatát jelenti. Az algoritmus logikáját kell a programban végigkövetni, és megfigyelni, hogy a kettő egyező-e? Sokszor a programozó maga veszi észre a hibákat, miközben valakinek részletesen magyarázza. Formai ellenőrzés Szintaktika / szemantika A program minden utasítását végre kell hajtani legalább egyszer. Igen sok információt ad a programról ha a változóink felhasználásáról készítünk egy táblázatot. Tartalmi ellenőrzés, ellentmondás keresés Ilyen hiba lehet az, ha egy változónak értéket adunk, de ezután nem használjuk semmire, vagy közvetlenül utána még egyszer értéket kap. Előfordulhat, hogy egy utasításhoz soha nem jut el a program. Illetve, ha kezdőérték nélkül használunk egy változót kifejezésben. Dinamikus tesztelési módszerek: Alapelv, hogy a programot működés közben vizsgáljuk Fekete doboz módszer a kód ismerete nélkül tesztel Ekvivalencia osztályok keresése Mivel a a kimerítő bemeneti tesztelés gyakorlatilag megvalósíthatatlan, meg kell elégednünk a bemenő adatok szűk részhalmazával való teszteléssel. Azért, hogy a részhalmaz minél hatásosabb legyen a benne szereplő tesztesetekre teljesüljenek a következők: - Minden tesztesetnek annyi bemeneti feltételt kell kielégítenie, amennyit csak lehetséges, hogy ezzel a szükséges tesztesetek számát csökkentsük. - A bemeneti tartományt valamilyen módon részekre kell bontani, és ezekre a részekre jellemző teszteseteket kell választani Ezekre a részekre legyen igaz az, ha egy ilyen osztályból választunk egy tesztesetet, és ezzel hibát találunk a programban, akkor az osztály más elemét választva is nagy valószínűséggel hibát találnánk, illetve, ha a kiválasztott tesztesetre a program jól működik, az osztály más elemét választva is nagy valószínűséggel helyes eredményt adna. MEGJEGYZÉS: Ekvivalencia osztályokat nem csak az érvényes, hanem az érvénytelen adatokhoz is létre kell hozni, és azokkal kipróbálni. Határeset elemzés - Az ekvivalencia osztály kiválasztott elemének a határon lévő elemeket választja - Nem csak bemeneti, hanem kimeneti ekvivalencia osztályt is figyelembe veszi Pl. ha a rendeznünk kell 1..128 számot, akkor célszerű kipróbálni 0,1,128,129 adatokkal vagy ha a bemeneti tartomány (0,1) nyílt intervallum, akkor a 0,1,0.01,0.99 értékekkel érdemes kipróbálni a programot Fehér doboz módszer a kód ismeretének felhasználásával Utasítások egyszeri lefedésének elve A módszer lényege olyan tesztesetek kiválasztása, amelyek alapján minden utasítást legalább egyszer végrehajtunk a programban. Bár ez sokszor jó módszer, de nem tökéletes : Ha X>0 akkor ki: X 14
Ebben a példában egyetlen próbával elérhetjük az összes utasítás végrehajtását (pl. X=1), de ezzel a próbával nem derülne ki az, ha az X>0 feltétel helyett az X>=0 szerepelne, azaz a program esetleg hibás lenne. Döntéslefedés elve A programban minden egyes elágazást igaz illetve hamis ágát legalább egyszer be kell járni a tesztelés során. A döntéslefedés elvét figyelembe véve eleget teszünk az utasításlefedés követelményének is. Itt is maradnak azonban problémák: Ha X>0 vagy Y>0 akkor ki: X*Y Ebben az esetben az (X=1, Y=1) és az (X=-1,Y=-1) tesztesetek lefedik a döntéseket, de nem vennénk észre velük, ha a második feltételt (Y>0) rosszul írtuk (vagy lehagytuk) volna. Feltétellefedés elve Ebben az esetben a döntésekben szereplő minden feltételt legalább egyszer hamis illetve igaz eredménnyel értékelünk ki. Ha X>0 és Y>0 akkor ki: X*Y Itt az (X=1, Y=1) és az (X=-1,Y=-1) tesztadatok elégségesek ezen elv megvalósításához, de az elágazás igaz ágát egyiknél sem hajtjuk végre, s így ez az előző elv követelményét nyilvánvalóan nem teljesíti. Döntés- vagy feltétellefedés elve Az előző pontban lévő példából látható, hogy a feltétellefedés követelményét kielégítő tesztesetek nem feltétlenül elégítik ki a döntéslefedés követelményét. Ezért az előző két elvet egyesítő módszert kell készíteni úgy, hogy mindkét elv érvényesüljön. Stressz teszt Olyan esetben alkalmazzuk, mikor a program nagy adatmennyiséggel dolgozik, vagy fontos, hogy az adott feladatot meghatározott időn belül elvégezze. Próbáljuk ki a programot nagy adatmennyiséggel, ha mód van rá, akkor a felhasználó minél gyakoribb beavatkozásával. 1.2.5. Dokumentálás Minden fázisnak megvan a maga "terméke", dokumentációja A program elkészülte után is meg kell őrizni valamennyi fázis dokumentációját, ez megkönnyíti a későbbi esetleges bővítések, módosítások kivitelezését. A program fejlesztését végigkísérő dokumentáció összességét fejlesztői dokumentációnak nevezzük, ezt a felhasználó nem, csak a fejlesztő cég őrzi meg. A fejlesztői dokumentáció részei: - Feladat specifikáció - Programterv - Forrásprogram - Kész program (telepíthető és futtatható program) - Tesztadatok listája, a tesztelés jegyzőkönyvei - Fejlesztési lehetőségek A kész rendszerhez elkészül a felhasználói dokumentáció, amelyet a felhasználó kap meg. A felhasználói dokumentáció részei: - A program által megoldható feladat leírása, az alkalmazás korlátjai - A szükséges hardver környezet leírása. Számítógép típusa, a minimálisan szükséges konfiguráció, amely a program futtatásához szükséges. - Szoftver környezet. Operációs rendszer, a futtatáshoz szükséges esetleges kiegészítő szoftverek. - Fejlesztői környezet, programozási nyelv. - A program telepítése, betöltése, futtatása. - A program használatának részletes leírása: speciális billentyűk használata, az esetlegesen használt ikonok, szimbólumok jelentése, funkciója, a menürendszer ismertetése, segítségkérés lehetőségei. A működés leírása minden részletre kiterjedően, az egyes funkciók elérésének módja. - Milyen kérdésre milyen válasz adható - Képernyőtervek, listatervek. - Hibaüzenetek felsorolása, értelmezése, teendők a hiba elhárítására. - Biztonsági előírások (pl. adatok időszakos mentése). - Felhasználói jogosultságok, esetleges jelszó megadási módok. - Ki és milyen feltételekkel jogosult a szoftver használatára (Licence szerződés). 15
1.2.6. A szoftver élete Amikor a felhasználó használni kezdi a letesztelt és hibátlannak vélt programot, szinte biztosra vehetjük, hogy lesz még dolga a fejlesztőnek. Egyrészt a leggondosabb tesztelés mellett is előfordulhatnak rejtett hibák, olyan esetek, melyekre a programfejlesztő nem gondolt, de a használat során kiderül, nem megfelelően működik a program. Másrészt a feladat elemzése során nem mindig sikerül a felhasználó minden igényét feltárni, később merülnek fel megoldandó rész-feladatok. A felhasználó többnyire akkor érez rá a számítógép által élvezhető előnyökre, amikor már egy ideje használja a megrendelt és elkészült programját. Így utólagos bővítésekre, javításokra szinte mindaddig szükség van, amíg a programot használják és rendelkezésre áll a fejlesztői gárda, a fejlesztői dokumentáció. Egy szoftver javított, illetve továbbfejlesztett változatait verziószámmal szokás ellátni. Nagyobb változtatások esetén a verziószám egészrésze, kisebb javításoknál, módosításoknál a verziószám törtrésze változik. Ellenőrző kérdések: Hogyan kell elkezdeni egy feladat megoldását? Hogyan kell megtervezni egy programot? A fejlesztői dokumentáció milyen elemekből áll? Mire kell kitérnie a felhasználói dokumentációnak? Mit jelentenek a következő fogalmak: - Analízis - Feladat specifikáció - Fejlesztői dokumentáció - Felhasználói dokumentáció - Kódolás - Tesztelés - Felhasználóbarát. 2. Összetett adatszerkezetek Eddigi példáinkban olyan változókat használtunk, amelyek egyetlen érték (skalár) tárolására alkalmasak. A változók deklarálásánál megismerkedtünk a skalár típusokkal. Már eddig is találkoztunk olyan feladatokkal, ahol egyszerre több értékkel kellett dolgoznunk, például a csoport zh - átlagának kiszámításakor. Ha az egyes zh - eredményeknek az átlagtól való eltérésére is kíváncsiak vagyunk, akkor a pontszámokat tárolni kell, nem elég pusztán az adatok bevitelekor számolni azokkal. Ebben a fejezetben megtanuljuk, hogyan használjunk különböző adattípusokat több értéknek ugyanabban a változóban való tárolására. C-ben: Elemi adat-típusok: karakter egész lebegőpontos dupla lebegőpontos double érték nélküli char int float void Typedef: szinoním típusnevek bevezetéséhez Enum: felsorolt típusok csoportos, egymással kapcsolatban álló konstansok létrehozására pl. enum szin {piros, feher, zold, kek, sarga}; enum valasz {igen, nem}; Összeállított (aggregate) típusok. Tömb,struktúra, bitmező, union Tömb definíció: Összetett adatszerkezet, véges számú azonos típusú elemek összessége. 2.1. Egydimenziós tömbök Áttekintés: 16
A tömb olyan összetett adatszerkezet, amely lehetővé teszi, hogy egyetlen változó több értéket tároljon. A tömb (array) típus olyan objektumok halmaza, amelyek azonos típusúak és a memóriában folytonosan helyezkednek el. Amikor deklarálunk egy tömböt, meg kell adnunk a tárolandó érték típusát és a tételek (tömb elemek) maximális számát. Egy tömbben minden elemnek ugyanolyan típusúnak kell lennie, Egy értéknek a tömbben való tárolásához meg kell adjuk a tömb nevét és az elem tömbön belüli sorszámát, melynél az értéket tárolni akarjuk. A tömb első eleme az 1. sorszámú, de a C nyelvben az indexek nullától indulnak! Az elemek elérése a tömb nevét követő indexelés operátorban megadott elemsorszám (index) segítségével történik. Egy tömb deklarálásakor megadhatjuk az egyes elemek kezdeti értékét, éppúgy, mint a skalár típusoknál. A leggyakrabban használt tömbtípus egyetlen kiterjedéssel (dimenzióval) rendelkezik. Az egydimenziós tömböket szokás vektoroknak is nevezni. Többdimenziós tömbök (mátrix) használatára is van lehetőség, ezek elemeinek tárolása a memóriában C++ esetén sorfolytonosan történik. A tömböket deklarálnunk kell. Az egydimenziós tömb deklarálásának általános alakja: típus tömbnév [méret]; ahol típus az elemek típusát definiálja, a tömbnévre ugyanazok a szabályok vonatkoznak, amelyeket a változónevekkel kapcsolatban már megismertünk, a méret pedig a tömb elemszámát adja. A méretnek a fordító által kiszámítható konstans kifejezésnek kell lennie. Az elemek indexelése C-ben 0-tól méret-1-ig történik. A tömb elemeinek egymás után történő elérésére általában a for ciklust használjuk, melynek ciklusváltozója a tömb indexe. 2.1.1. feladat Készítsen programot, amely bekéri a csoport zh - eredményének pontszámait, kiszámítja az átlagot, majd kiírja - sorszámozva - az egyes pontszámokat és az átlagtól való eltérésüket. Megoldás: Elemezzük a feladatot Tudnunk kell, hogy hány főből áll a csoport. Az aktuális csoport-létszámot a program futásakor is megadhatjuk, de - mivel a pontszámokat tárolnunk kell egy egydimenziós tömbben - a csoport lehetséges (maximális) létszámát már a forráskód megírásakor ismerni kell, legyen ez a példánkban 20 fő. Pszeudokód: Program Változók: letszam az aktuális létszám, pozitív egész cv ciklusváltozó a létszámhoz, pozitív egész pontszam [20] a pontszámok tárolására, elemei egész típusúak atlag valós típusú, a pontszámok átlaga atlag = 0 ciklus Be: letszam //1..20 amíg letszam<1 vagy letszam>20 ciklus vége ciklus cv = 0 kezdőértéktől cv = letszam-1 végértékig 1 lépésközzel Be: pontszam [cv] ciklus vége // beolvasás ciklus cv = 0 kezdőértéktől cv = letszam-1 végértékig 1 lépésközzel 17
atlag = atlag + pontszam [cv] ciklus vége atlag = atlag / letszam Ki: atlag //átlagszámítás ciklus cv = 0 kezdőértéktől cv = letszam-1 végértékig 1 lépésközzel Ki: cv+1, pontszam [cv], pontszam [cv] - atlag atlag = atlag + pontszam [cv] ciklus vége program vége Amikor tömböket használunk a C++-ban, figyelni kell arra, hogy a tömb első elemének sorszáma (indexe) mindig 0. A mindennapi gyakorlatban viszont megszoktuk, hogy többnyire 1-2.1.2. feladat A menzán 10-féle étel közül választhatunk, mindegyiknek meg van adva a kalóriaértéke. Készítsen programot, amely "ismeri" a sorszámmal ellátott ételek kalóriaértékét, s a választott adagok alapján megmondja, hogy összesen mennyi kalóriát képvisel az ebédünk. Megoldás: Elemezzük a feladatot A program "ismeri" a kalóriaértékeket, ez azt jelenti, hogy konstansként fogjuk azokat a programba beépíteni. Input adatként adjuk meg, hogy az egyes ételekből mennyit vettünk meg, ez lehet 0, 0.5, 1, vagy egynél nagyobb egész szám. Két egydimenziós tömböt (vektort) fogunk deklarálni, az összes kalória értékét megkapjuk, ha az azonos sorszámú tömb-elemeket összeszorozzuk és a kapott szorzatok összegét képezzük. (Ez nem más, mint a két vektor skaláris szorzata!) Az alábbi kalóriatáblázat alapján fogunk dolgozni: sorszám étel megnev. kcal 0 zöldségleves 26 1 zöldborsóleves 110 2 halászlé 156 3 vagdalt 168 4 párolt csirke 164 5 natúrszelet 206 6 kelkáp.főzelék 138 7 tökfőzelék 141 8 párolt zöldség 202 9 burgonyapüré 214 Pszeudokód: Program Változók: energia [10] = [26,110,156,168,164,206,138,141,202,214] adag [10] valós típusú tömb ossz_energia pozitív egész, i ciklusváltozó, egész Ciklus i=0 kezdőértéktől i = 9 végértékig 1 lépésközzel Be: adag [i] Ciklus vége ossz_energia = 0 Ciklus i=0 kezdőértéktől i = 9 végértékig 1 lépésközzel ossz_energia = ossz_energia + adag [i] * energia [i] Ciklus vége 18
Ki : ossz_energia Program vége 2.2. Stringek A stringek, vagy karakterláncok olyan információt tárolnak, mint például személynevek, elnevezések, könyv címek. A C nyelv nem rendelkezik önálló string típussal, a karakterláncokat olyan char típusú egydimenziós tömbben tárolja, melynek utolsó, még a karakterlánchoz tartozó eleme után egy \0 (ASCII 0) karakter áll. 2.3. Többdimenziós tömbök A többdimenziós tömbök deklarációjának általános alakja: típus tömbnév [méret1][méret2]... [méretn]; ahol minden dimenzióra külön zárójelezve kell megadni a méreteket. A dimenziók számára semmilyen korlátozást nem tartalmaz a C nyelv definíciója. Ebben a fejezetben csak a kétdimenziós tömbökkel foglalkozunk. 2.3.1. feladat Egy háromfős társaság minden tagja 2-2 lottószelvényt vásárol minden héten. 5-ös lottón, állandó számokkal játszanak. Készítsen programot, amely tárolja a szelvényeken megjátszott tippeket, bekéri a nyerőszámokat és megmondja, hogy volt-e ötös a szelvények között. Megoldás: Elemezzük a feladatot Összesen 6 szelvényünk (tipp-sorunk) van, egy-egy tipphez öt darab egész szám tartozik. Mivel a társaság állandó számokkal játszik, a tippeket előre megadhatjuk a programban. Bekérni csak a nyerőszámokat kell. A 6 szelvényen szereplő tippsor tárolása egy kétdimenziós tömbben történik, melynek egy-egy sora tartalmaz egy tippsort, példaként vegyük az alábbi tippeket: 1. tipp: 11, 34, 45, 66, 89 2. tipp: 3, 13, 43, 52, 78 3. tipp: 25, 31, 72, 86, 90 4. tipp: 8, 15, 26, 48, 81 5. tipp: 19, 29, 39, 49, 69 6. tipp: 21, 32, 43, 54, 65 A C++-ban az alábbi tömböt fogjuk deklarálni a fenti "számhalmaz" tárolására: int tippek [6][5]; vagyis deklarálunk egy int típusú elemeket tartalmazó kétdimenziós tömböt, amelynek 6 sora (első index) és öt oszlopa (második index) van. Az egydimenziós tömbök tárgyalásánál azt mondtuk általánosan, hogy a tömb típus olyan objektumok halmaza, amelyek azonos típusúak és a memóriában folytonosan helyezkednek el. Itt a tippek tömb elemei mind integer típusúak és a memóriában úgy helyezkednek el egymás után, hogy az első sor elemei után 19
következnek a második sor elemei, és így tovább, vagyis a kétdimenziós tömb elemeit a C nyelv sorfolytonosan tárolja. Ezt azért is fontos tudni, mert ha a deklarálás során kezdeti értéket is adunk az elemeknek, akkor az elemek értékeinek sorrendje ezt kell, hogy kövesse. Pszeudokód: Program Változók: tippek [6][5] a tippeket tartalmazó tömb, elemei pozitív egész számok, melyeket a deklaráláskor kell megadni nyeroszamok [5] a nyerőszámokat tartalmazó 5 elemű tömb i, j ciklusváltozók, pozitív egész számok egyezik segédváltozó, egész típusú vanotos 0 ha nincs ötös, 1, ha van ötös találat a tippek között Ciklus i=0 kezdőértéktől i= 4 végértékig Be: nyeroszamok [i] Ciklus vége vanotos = 0 Ciklus i=0 kezdőértéktől i = 5 végértékig egyezik = 0 Ciklus j = 0 kezdőértéktől j = 4 végértékig egyezik = egyezik + (tippek [i][j] == nyeroszamok [j]) Ciklus vége // j Ha egyezik == 5 kiugrás a ciklusból Ciklus vége //i Ha egyezik == 5 Ki: "Van ötös!" egyébként Ki: "Nincs ötös!" program vége A kétdimenziós tömbök leggyakoribb felhasználási területe a matematikában használt mátrixok tárolása, műveletek mátrixokkal. 2.3.2. feladat Készítsen programot, amely egy maximum 20 fős tanulócsoport zh eredményeit tárolja egy tárgyból. A csoport öt zh-t ír. Az adatbevitel úgy történik, hogy megadjuk a zh sorszámát, majd az egyes tanulók pontszámait sorban egymás után. A program minden zh-ra kiszámítja az átlagot és le is tárolja azt. A programnak tárolnia kell a hallgatók neveit is. Megoldás: Elemezzük a feladatot A tömbökről már a bevezetőben azt mondtuk, hogy azonos típusú elemeket tárolnak. A hallgatók neveit és a zh-pontszámokat tehát más más tömbben fogjuk tárolni. A neveket tároló tömb egy sora egy hallgató nevének karaktereit tárolja. Pszeudokód: Program Változók: i, j, letszam egész számok nevek [20][31] char típusú tömb, soronként egy nevet tartalmaz pontok [20][5 ] int típusú tömb, egy sora egy hallgató zh-pontszámait tartalmazza atlag [5] = [0,0,0,0,0] valós típusúak, a zh átlagokat tartalmazzák 20
Be: letszam Ciklus i=0 kezdőértéktől i=letszam-1 végértékig 1 lépésközzel //Névsor beolvasása Be: nevek [i] Ciklus vége Ciklus i=0 kezdőértéktől i=letszam-1 végértékig 1 lépésközzel Ki: nevek [i] Ciklus j=0 kezdőértéktől j=5-1 végértékig 1 lépésközzel Be: pontszam [i][j] Ciklus vége Ciklus vége Ciklus i=0 kezdőértéktől i=letszam-1 végértékig 1 lépésközzel Ciklus j=0 kezdőértéktől j=5-1 végértékig 1 lépésközzel atlag [j] = atlag [j] + pontszam [i][j] Ciklus vége Ciklus vége //Pontszámok beolvasása //Átlag számítás Ciklus j=0 kezdőértéktől j=5-1 végértékig 1 lépésközzel // pontszám átlagok kiírása Ki: atlag [j] / letszam Ciklus vége Program vége 2.4. A rekord fogalma Tömb: azonos típusú objektumok összessége Feladat: árukészlet nyilvántartása adat neve típus megnevezés char [30] EAN kód long int mennyiségi egység char [10] mennyiség double beszerzési ár nettó float eladási ár nettó float ÁFA kulcs int Tárolás: tömbökben? memóriában? Rekord (C-ben struktúra) típus: több, tetszőleges típusú (kivéve a void és a függvény típust) objektum együttese. Ezek az objektumok önálló, a rekordon (struktúrán) belül érvényes nevekkel rendelkeznek. Az objektumok szokásos elnevezése a C-ben: struktúraelem, vagy adattag más nyelvekben: mező Ellenőrző kérdések A többdimenziós tömbök deklarációjának általános alakja a C nyelvben. Egydimenziós tömb deklarálásának általános alakja. Kétdimenziós tömböknél mit jelent a sorfolytonos tárolás? 21