Nagy Krisztián
Programozás a modellező informatikus szakirányon
Készült: Veszprémi Anna táblás gyakorlatai Hudoba Péter géptermi gyakorlatai alapján
Szerkesztette: Nagy Krisztián
1. kiadás
A jegyzet módosítások nélkül szabadon terjeszthető!
A jegyzet az Eötvös Loránd Tudományegyetem – Informatikai Kar azon hallgatóinak készült, akik a Programtervező Informatikus szakon Modellező informatikus szakirányon nappali képzésben tanulnak.
Tartalomjegyzék Elmélet 1. Táblás gyakorlatok............................................................................................................................. 3 1.1 Bevezetés ..................................................................................................................................... 3 1.2 Intervallumos programozási tételek ............................................................................................. 6 1.3 Intervallumos programozási tételekre visszavezethető feladatok ................................................ 7 1.4 Összetettebb feladatok intervallumos programozási tételekkel ................................................ 24 1.5 Felsorolós programozási tételek ................................................................................................. 36 1.6 Felsorolós programozási tételekre visszavezethető feladatok ................................................... 37 1.7 Típus specifikáció ........................................................................................................................ 42 1.8 Szekvenciális inputfile................................................................................................................. 43 1.9 Egyedi felsorolók készítése ......................................................................................................... 47 1.10 Összefuttató felsoroló .............................................................................................................. 52 Kódolás elmélet és gyakorlat (C++ programozási nyelven) 2. Géptermi gyakorlatok...................................................................................................................... 57 2.1 Intervallumos programozási tételek megvalóstása .................................................................... 57 2.2 Függvények használata C++ programozási nyelvben .................................................................. 60 2.3 Összetettebb feladat megoldása intervallumos programozási tételek segítségével .................. 63 2.4 Hibakezelés ................................................................................................................................. 66 2.5 Csomagokra bontás .................................................................................................................... 67 2.6 Struktúrák ................................................................................................................................... 68 2.7 Osztályok .................................................................................................................................... 69 2.8 A Deltoid osztály megvalósítása ................................................................................................. 73 2.9 Szekvenciális I/O ......................................................................................................................... 84 2.10 Összefuttató felsorolók megvalósítása ..................................................................................... 89
3. Segédanyagok .................................................................................................................................. 96 4. Felhasznált irodalom (Források) ...................................................................................................... 97
2
1. Táblás gyakorlatok 1.1 Bevezetés Program terve:
Specifikáció Visszavezetés Algoritmus (Struktogramm)
Bevezető feladat: (Programozási alapismeret-ről átállás Programozás-ra)
Adott két nem negatív egész szám, számítsuk ki a szorzatukat úgy, hogy a szorzás műveletét nem használjuk! Programozási alapismereteken tanult specifikációs módszer: Bemenet: Kimenet: Előfeltétel: Utófeltétel: Programozáson használt specifikációs módszer: állapottér (csak a bemeneti és kimeneti paramétereket tartalmazza) előfeltétel (megjelennek benne a bemenetekre vonatkózó kezdőértékek, amennyiben vannak) – A bemenő adatokra kirótt feltételeket tartalmazzák utófeltétel – A kimenő adatokra kirótt feltételeket tartalmazzák Ezek alapján a fentebbi feladatot így lehetne felírni: (Megjelennek a matematikában használt jelölések) ( - az adott kezdőértékek)
3
1. feladat: Az adott algoritmus alapján specifikáld a programot és határozd meg, hogy mit csinál!
Egy program sorról sorra fut le. Vegyünk két tetszőleges számot és nézzük meg, hogy mi történik. Legyen Ekkor:
Ha megnézzük az az értékeit.
értékeit, akkor jelenleg:
ez egy csere program, mely felcseréli
Specifikáció:
4
2. feladat: Adott egy pozitív egész szám. Adjuk meg a hozzá legközelebb álló prímszámot! Specifikáció:
Feladat: Specifikáld: Adott egy összetett természetes szám. Adjuk meg egy valódi osztóját!
Feladat: Specifikáld: Adott egy összetett természetes szám. Adjuk meg az összes valódi osztóját!
Megjegyzés:
:= összefűzés (asszociatív, bal oldali 0-elemes
művelet.
Feladat: Adott egy
alakú egyenlet, melyben
azonosság
nem lehet 0 ha és
tetszőleges valós számok. Keressük az -et.
5
Specifikáció: (Megjegyzés:
karakter sorozat – string)
Más féle utófeltétellel felírva:
1.2 Intervallumos programozási tételek
Az intervallumos programozási tételek megtalálhatóak Gregorics Tibor weboldalán. Cím: http://people.inf.elte.hu/gt/prog/prog.html A segédanyagok menüpont – Programozási tételek cím alatt (1.,2.,3. link) Továbbá az előadás fóliák között is található egy változat.
6
1.3 Intervallumos programozási tételekre visszavezethető feladatok 1. feladat Adott egy sakktábla és rajta két bástya, tegyünk le úgy egy harmadik bástyát, hogy mind a kettőt üsse!
Lehetőségek:
-
Nem állhatnak azonos mezőn
Specifikáció:
ne legyenek azonos mezőn a bástyák
Magyarázat Uf-hez: 1.sor: A logikai „l” változót definiáljuk, azaz leírjuk azokat a lehetős geket, amelyek teljesül se mellett igaz lesz a változó. Sorra: Ha a k t lehelyezett bástya x helyzete megegyezik, akkor az y rz kek távolsága nagyobb legyen, mint 1. (Az adott x vonalban ne állhasson egymás mellett a k t előre lehelyezett bástya) Vagy ugyan ez ne tört nhessen meg az y vonalában vagy (v gezetül) ne lehessen egymásra tenni a k t bástyát. (Ne állhasson egymáson a k t bástya. 2.sor: Megadja a módszer t, hogyha a logikai „l” változó rt ke igaz, akkor a programunknak mit kelljen csinálnia ahhoz, hogy ki tudja számolni a 3. bástya helyzet t. Itt ebben a sorban a fenti első ábrára ad megoldást. 3. sor: Negyedik ábrára nyújt megoldást 4. sor: Második ábrára nyújt megoldást. Továbbá a minimum/maximum-os megoldás kiküszöböli a harmadik ábrán látott rossz esetet.
7
Összegzés tételre visszavezethető feladatok 1. feladat: Egy nap folyamán n-szer megmértük a hőmérsékletet. Adjuk meg az átlaghőmérsékletet! Specifikáció:
Visszavezet s: összegz s t telre
Magyarázat: Specifikáció alapján vezettük vissza az összegz s t tel re. van egy n dimenziós tömbünk (h), aminek a dimenziója meg van határozva (ha 2, akkor 2 hőm rs klet szerepel benne, ha 3 akkor 3, ha n, akkor n…). A visszavezet s l nyege, hogy az általunk használt változókat a specifikációban, ráhúzzuk az adott t telben már tanult változókra. Jelen esetben a tömböt 1-től nig definiáltuk. (Lásd Utófelt tel szumma.) Így a programozási t telünkbe az m:=1-el. Továbbá az f(i)-t felváltja a h[i], mivel jelen esetben egy tömb adott rt keit szeretn nk összegezni.Maga az s változót m g nem használtuk ki, ez rt hagyjuk, hogy az összeg változója ez legyen. (Látható az utófelt telből, hogy a:= s/n) továbbá a műveletünk a +, aminek a nulleleme/semleges 0. Ez rt ezeken nem változtatunk. Most a megfeleltet sek alapján kell megalkotnunk az algoritmust. Az Összegz s algoritmusát használjuk. A dolgunk csak annyi, hogy a visszavezet sben használt változásokat írjuk be az algoritmusba s a v g re hozzá tesszük az átlag számítást.
Algoritmus:
8
2. feladat:
Adjuk meg két vektor skalárszorzatát! Specifikáció:
Visszavezet s: összegz s t telre
Algoritmus:
3. feladat:
Adott egy bankbetét indulóértéke. Évenként kamatos kamattal növekszik és évenként változó kamatlábbal n-évig. Mennyi lesz a betét végértéke? Segítség: Kamatos kamat:
Specifikáció:
(tizedes törtben kelljen megadni a kamatot (1-nél kisebb) ne százalékban)
9
Visszavezetés: Összegzés tételére
Megjegyz s: *: szorzás művelet. Szorzás nulleleme/semleges eleme: 1
Algoritmus:
A szorzás asszociativitása miatt
Visszavezetés: Összegzés tételére
Algoritmus:
10
Az x tartalmazza a betétet (végeredménynél)
Visszavezetés: Összegzés tételére Utófeltétel módosítása:
Visszavezetés: Összegzés tételre
Algoritmus:
Az első lépés az előfeltételünk miatt teljesül, így elhagyható. A továbbiak a második megoldásból adódnak. Így az algoritmusunk:
Kiválogatásos feladatok 1. feladat: Adott n nap átlaghőmérséklete. Adott melyik napokon volt fagypont alatt a hőmérséklet. Vállogasd ki! (A) megoldás: Legyen az eredmény egy sorozat! Specifikáció:
11
Visszavezet s: összegz s t telre
Megjegyzés:
művelet az összefűzést szokta jelenteni. Semleges eleme/nulleleme a <> .
Algoritmus:
(B) megoldás: Legyen a napok tömb! Specifikáció:
Visszavezet s: összegz s t telre
Algoritmus:
12
Feladat (Érdekességből): Az alábbi feladat megoldások hatékonyságból rosszak, így nem ajánlatos ezeket a megoldásokat használni a való életben. Csupán azt szeretnék prezentálni, hogy ezt a feladatot is vissza lehetne vezetni az Összegzés tételre több féleképpen.!
Legyen
Utófeltételben szerepelhető megoldási részek (nem a teljes (a)
):
Visszavezetésben:
(b) (c) Számlálás tételre visszavezethető feladat 1. feladat: Adott n pont egy koordináta-rendszerben. Hány pont esik az x és hány pont esik az y tengelyre? Megoldás: Specifikáció:
Magyarázat: 1. Hogyan képzeljük el az dimenziós tömböt és hogy helyezzük el benne az adatokat: Jelen esetünkben az 1. oszlop a pontok x koordinátáival, míg a 2. oszlop a pontok y koordinátáival van feltöltve. 2. Utófeltétel sorra: Mikor van az adott pont az x tengelyen, akkor ha az y=0. Az 1.-es pont alapján ez a mi esetünkben akkor teljesül, ha . Mikor van az adott pont az y tengelyen, akkor ha az x=0. Az 1.-es pont alapján ez a mi esetünkben akkor teljesül, ha . A számlálás tétel pedig az adott esetben azt számolja meg, hogy hányszor teljesül a feltétel. Ebből adódóan a mi algoritmusunkban két számlálás lesz. Mivel csak betű paraméterekben és a feltételben aprócska változásokban tér el, így a visszavezetést egyben végeztük el.
13
Visszavezetés: Számlálás tételre
Algoritmus: Mivel ugyan azt az intervallumot járja be a két megszámlálás és a c-k egymástól függetlenek, így a két megszámlálást egymásba lehet tenni.
Vegyes feladatok Adott f -n integrálható. n-részre kell bontani. Adjuk meg az integrál közelítőértékét!
Specifikáció:
n részre bontjuk
Visszavezetés: Összegzés tételére
14
Algoritmus:
Add meg a páros
Adott
értékek átlagát egy
intervallumon.
Specifikáció:
Visszavezetés: Összegzés tételre és számlálás tételre (Későbbiekben lehet beágyazással is) Összegzés:
Számlálás:
Algoritmus: Jelenleg használt
15
Beágyazásos módszerrel:
Feladat: Milyen programozási tételre vezetnéd vissza az alábbi feladatokat? 1. feladat: Adott egymás utáni nap átlaghőmérséklete. Hányszor csökkent több mint 5 kal az átlaghőmérséklet? 2. feladat: Adott egy természetes szám és egy intervallumban prímszám, ha igen mi az? 3. feladat: Adott egy
egész. Van-e az
egész szám, prímszám-e?
4. feladat: Adott a síkon 5. feladat: Adott
-
pont a koordinátáival, melyik esik a legtávolabb az origótól?
szó, adjuk meg a leghosszabb
betűvel kezdődő szót.
6. feladat: Adott két természetes szám, adjuk meg a legnagyobb közös osztójukat. 7. feladat: Adott egy függvény, és egy egész. Tudjuk, hogy az intervallumon monoton növekszik. Felveszi-e az intervallumon a értéket, ha igen hol? Megoldások: 1. feladat: Számlálás; 2. feladat: Lineáris keresés (Pesszimista); 3. feladat: Lineáris keresés (Optimista); 4. feladat: Maximum kiválasztás; 5. feladat: Feltételes maximum keresés; 6. feladat: Kiválasztás; 7. feladat: Logaritmikus keresés (vagy kevésbé hatékony Lineáris ker.)
16
Feladatok: 1. feladat: Adott egy természetes szám és egy intervallumban prímszám, ha igen mi az?
egész. Van-e az
Specifikáció:
Részletes utófeltétel:
Visszavezetés: Lineáris keresésre (pesszimista eldöntéssel)
Algoritmus:
2. Feladat:
Adott egy
egész szám, prímszám-e?
Specifikáció:
Utófeltétel másik felírással:
Visszavezetés: Lineáris keresés tételre (optimista eldöntéssel)
17
Algoritmus:
3. Feladat:
Adott a síkon
pont a koordinátáival, melyik esik a legtávolabb az origótól?
Specifikáció: - Pont record, ha
, akkor az alábbi módon tudunk hivatkozni az
egyes mezőkre:
Visszavezetés: Maximum kiválasztás (Maximum keresés)
Algoritmus:
18
4. Feladat:
Adott
szó, adjuk meg a leghosszabb
betűvel kezdődő szót.
Specifikáció:
Feltételben: Másik felírásban a feltétel lehetne:
(prefix)
Visszavezetés: Feltételes maximum keresés tételére
Algoritmus:
Feladat: Milyen tételekre vezethetőek vissza az alábbi feladatok? 1.Feladat: Határozzuk meg egy egész számokat tartalmazó tömb azon legkisebb értékét, amely -val osztva egyet ad maradékul. 2.Feladat: Adott nap átlaghőmérséklete és csapadékmennyisége. Mennyi azon napok átlaghőmérsékleteinek átlaga, amelyeken volt csapadék. 3.Feladat: Adottak az és vektorok, ahol y elemei az indexei közül valók. Keressünk az vektornak az -ban megjelölt elemei között páros számot. 4.Feladat: Igaz-e, hogy egy tömbben elhelyezett szöveg odafelé és visszafelé olvasva is ugyanaz? 5.Feladat: Adott a síkon néhány pont a koordinátáival, és egy körlemez a középpont koordinátáival és a kör sugarával. Adjunk meg egy olyan pontot, amely a körlemezre esik.
19
6.Feladat: Egymást követő napokon megmértük a déli hőmérsékletet. Hányszor mértünk 0 úgy, hogy közvetlenül utána fagypont alatti hőmérsékletet regisztráltunk? 7.Feladat: A Föld felsznének egy vonala mentén egyenlő távolságonként megmértük a terep tengerszint feletti magasságát (méterben), és a mért értékeket egy tömbben tároljuk. Melyik a legalacsonyabb hegycsúcs a mérési sorozatban. 8.Feladat: Egy hegyoldal hegycsúcs felé vezető ösvénye mentén egyenlő távolságonként megmértük a terep tengerszint feletti magasságát, és a mért értékeket egy vektorban tároljuk. Megfigyeltük, hogy ezek az értékek egyszer sem csökkentek az előző értékekhez képest. Igaz-e, hogy mindig növekedtek? 9. Feladat: Adott két tömb: egy és egy . Fektessük a -t folyamatosan egymás után, és ezt helyezzük az tömb mellé. Hány esetben kerül egymás mellé ugyanaz az érték? 10. Feladat: Állítsuk elő egy természetes szám összes valódi osztóját! Megoldások: 1. Feltételes maximum keresés; 2. Összegzés + Számlálás; 3. Lineáris keresés (pesszimista); 4. Lineáris keresés (Optimista); 5. Lineáris keresés (Pesszimista); 6. Számlálás; 7. Maximum keresés; 8. Lineáris keresés (Optimista); 9. Számlálás; 10. Összegzés ( összefűzés művelettel)
1. Feladat: Határozzuk meg egy egész számokat tartalmazó tömb azon legkisebb értékét, amely -val osztva egyet ad maradékul. Megoldás: Specifikáció:
Visszavezetés: Feltételes maximum keresés tételére
20
Algoritmus:
2. Feladat: Adottak az és vektorok, ahol y elemei az megjelölt elemei között páros számot.
indexei közül valók. Keressünk az
Specifikáció:
Visszavezetés: Lineáris keresés (pesszimista) tételére
Algoritmus:
21
vektornak az -ban
3. Feladat: Igaz-e, hogy egy tömbben elhelyezett szöveg odafelé és visszafelé olvasva is ugyanaz?
Specifikáció:
Visszavezetés: Lineáris keresés (optimista) tételre
Algoritmus:
22
4. Feladat: Adott két tömb: egy és egy . Fektessük a -t folyamatosan egymás után, és ezt helyezzük az tömb mellé. Hány esetben kerül egymás mellé ugyanaz az érték?
Specifikáció:
Visszavezetés: Számlálás tételére
Algoritmus:
23
1.4 Összetettebb feladatok intervallumos programozási tételekkel Feladat: Adott egy -es egészeket tartalmazó mátrix. Határozzuk meg a legnagyobb sorösszegű sort. Van-e csupa oszlop?
Megoldás: Határozzuk meg a legnagyobb sorösszegű sort. -
-
-
Képzeljünk el egy
-es mátrixot:
Az első paraméter (m) jelöli a mátrix sorainak a számát, míg a második paraméter (n) a mátrix oszlopainak a számát jelöli. A feladatunk szövegéből is látszik, hogy ez már egy olyan feladat, amiben több programozási tétel is szerepel és ezek a tételek egymásba vannak ágyazva. Figyeljük meg a feladat szövegét. Emeljük ki belőle a kulcsszavakat: legnagyobb, sorösszeg. Mivel a legnagyobb esetén nem szeremel egyéb feltétel, ezért a Maximum keresés tételét kell majd alkalmaznunk, míg a sorösszeg kulcs szó, az összegzés tételére hívja fel a figyelmet. Fontos kiemelni, hogy egymásba ágyazott tétel esetén a belső tételt egy függvénnyel definiáljuk a specifikációban és az algoritmusban is megjelenhet paraméter átadás formályában. Ilyenkor a függvényt külön definiáljuk a specifikáció során, míg a külső tételt tartalmazó algoritmusban felhasználjuk ezt a függvényt egy adott paraméter átadás folyamán vagy akár feltételként is, viszont ekkor oda kell figyelnünk arra, hogy ezt a paraméter átadást vagy feltételt egy külön algoritmusban tisztázni kell. (Olyan, mint ha a programunkban lenne egy belső program.) Ekkor az algoritmust úgy kell látnunk egyben, hogy a Belső tételt tartalmazó algoritmusunk benne szerepel a külső tételt tartalmazó algoritmusunk azon „dobozában”, ahol az adott paraméter átadás vagy feltétel megtalálható.
24
Specifikáció:
Megjegyzés:
elhagyható, mivel
-ben
alapértelmezett
Most az utófeltételben specifikáltuk a külső tételt tartalmazó algoritmusunk, tehát még specifikálnunk kell a belső tételt tartalmazó bevezetett függvényünket:
Visszavezetés: Maximum keresésbe ágyazott összegzés Maximum keresés:
Összegzés:
Algoritmus: Külső tételt tartalmazó algoritmus:
Belső tételt tartalmazó algoritmus:
Itt jól látható, hogy a külső algoritmusban az belső algoritmust.
dobozka helyére kell betenni, a
25
Van-e csupa
oszlop?
Specifikáció: Megjegyzés: itt most az ind elhagyható
Visszavezetés: Lineáris keresésbe ágyazott optimista lineáris keresés Lineáris keresés:
Optimista lineáris keresés:
Algoritmus: Megjegyzés: Amennyiben kihagyjuk a külső tételből az ind változót, úgy az aloritmusunkban is ki kell törölni azokat a dobozokat, amelyek az ind paramétert tartalmazzák! Külső tételt tartalmazó algoritmus:
Belső tételt tartalmazó algoritmus:
Itt jól látható, hogy a külső algoritmusban az belső algoritmust.
dobozka helyére kell betenni, a
26
1. feladat: Adottak téglalapok. Igaz-e, hogy a kerületük növekvő számtani sorozatot alkot? Specifikáció:
Visszavezetés: Optimista lineáris keresés tételére
Algoritmus:
2. feladat: (Csak specifikáció)
Megoldás: Kiválasztással
27
2. rész: Logaritmikus keresés sémája és működése
Egy tömbre nézve, hogy működik:
1. Keressük a h=19-et:
2. Keressük a h=15-öt (Nincs ilyen elem a tömbbe most)
nem teljesül a ciklus feltétel
Nincs ilyen elem
28
A fenti képből adódóan látszik, hogy a Logaritmikus keresés sokszor hatékonyabb a Lineáris keresésnél. Egy 10 elemű tömb esetén, amennyiben a keresett érték az első helyen áll, úgy a Lineáris kereséssel hamarabb megtaláljuk. Amennyiben a tömbben a 2,3,4-es helyen található, úgy azonos hatékonysággal találja meg a két tétel. Amennyiben 5,6,7,8,9,10. helyen található a keresett elem úgy a Logaritmikus keresés a hatékonyabb. Miért hívják logaritmikus keresésnek? Azért, mert a maximális összehasonlítások száma: A fentebbi céltáblás feladat megoldása Logaritmikus kereséssel:
Kihasználjuk, hogy rendezetten vannak megadva a tömb elemei és így visszavezethetjük a logaritmikus keresés tételére a feladatunkat.
29
3. rész: Egymásba ágyazott tételes feladatok 1. feladat: Adott egy egészeket tartalmazó tömb. Melyik szám fordul elő a leggyakrabban a tömbben? Specifikáció:
Visszavezetés: Maximum keresésbe ágyazott számlálás Maximum keresés: (külső tétel)
Számlálás: (belső tétel)
30
2. Feladat: Adott s szöveg és m minta. Adjuk meg, mely pozíciókra illesztkedik a minta. Brute force
Specifikáció:
Visszavezetés: Összegzés tételébe ágyazott Optimista lineáris keresés Összegzés:
Optimista lineáris keresés:
31
Algoritmus:
1. Feladat: Adott egy koordinátarendszerben m pont. Melyik kettő van egymáshoz legközelebb?
Specifikáció:
32
Visszavezetés: Minimum keresésbe ágyazott minimum keresés Külső minimum keresés:
Belső minimum keresés:
Algoritmus:
2. Feladat: Adott egy -es egész mátrix, keressünk egy olyan sort, amelyben a legnagyobb páros szám osztható 3-mal. Specifikáció:
33
Uf-et így is fel lehet írni:
Visszavezetés: Lineáris keresésbe ágyazott Feltételes maximum keresés Külső tétel: Lineáris keresés
Belső tétel: Feltételes maximum keresés
Algoritmus:
34
3. Feladat: (Csak specifikációk és hatékonyság) Adott egy n hosszú egészekből álló vektor és egy k indexű elemei között olyan, amelyik nagyobb az összes
index. Van-e a vektor indexű elemnél?
Specifikáció:
Kevésbé hatékony algoritmusok:
- Lineáris keresésbe ágyazott optimista lineáris keresés - Hatékonysága:
- Optimista lineáris keresésésbe ágyazott lineáris keresés - Hatékonysága: Hatékonyabb algoritmusok:
- Maximum keresés és maximum keresés (Nem egymásba ágyazott!) - Hatékonysága:
- Maximum keresés visszafelé - Hatékonysága:
- Maximum keresés és optimista lineáris keresés (Nem egymásba ágyazott!) - Hatékonysága:
- Maximum keresés és lineáris keresés (Nem egymásba ágyazott!) - Hatékonysága:
35
1.5 Felsorolós programozási tételek
Az intervallumos programozási tételek megtalálhatóak Gregorics Tibor weboldalán. Cím: http://people.inf.elte.hu/gt/prog/prog.html A segédanyagok menüpont – Programozási tételek cím alatt (4. link) Továbbá az előadás fóliák között is található egy változat.
36
1.6 Felsorolós programozási tételekre visszavezethető feladatok 1. Feladat: Számoljuk meg egy n természetes szám páros valódi osztóinak számát
-
-
Fontos változás az intervallumos programozási tételekhez képest, hogy az utófeltételben nem köthetjük ki, hogy az előfeltétel teljesül További fontos változás, ami főleg az algoritmusban fog látszani, hogy felsorolós tételek esetén nem számlálós ciklusunk van, emiatt nem szabad elfelejtenünk tovább léptetni a felsorolónkat. Egyedi felsorolót (ami nem nevezetes) a visszavezetésben definiálnunk kell. Egy felsoroló esetén az alábbi „funkciókat” kell megadnunk: t.First(), t.Current(),t.Next(), t.End()
Jelen esetünkben: t.First() t.Next() t.Current() t.End() továbbá, mivel megszámlálás tételről van szó, így Algoritmus: -
Mindig a sablon tételünkre húzzuk rá az aloritmust
37
2. Feladat: - Halmazokra vonatkozó felsorós feladat Adott egy egész számokat tartalmazó halmaz, válogassuk ki a páros számokat egy halmazba, a páratlanokat egy vektorba. Jelölések halmazra:
vagy
Egészekből álló halmaz felsorolója:
t.First() – t.Next() (mem: member) t.Current() - determinisztikus: t.End()
nem determinisztikus (véletlenszerű) :
Összegzés tétele
Algoritmus:
38
3. Feladat: - Mátrixos feladatok
A felsoroló lehet: -
sorfolytonos (1. sor elemei, 2. sor elemei, …, m. sor elemei) oszlopfolytonos (1. oszlop elemei, 2. oszlop elemei, …, n. oszlop elemei)
Sorfolytonos felsoroló:
t.First() t.Current() t.Next()
t.End()
Nem sablon szerinti Uf és algoritmus:
39
Sablon szerinti Uf és algoritmus: Láthatjuk, hogy ez a megoldás nem annyira hatékony, ezért módosítsuk az algoritmusunk. Az így kapott eredmény egy sablon lesz. Ez egy olyan kivételes eset, ahol két egymásba ágyazott számlálós ciklust használunk. Negatívuma: - 1. elemet kétszer vizsgálja meg az algoritmus Pozitívum: Így is sokkal hatékonyabb, mint a fentebbi
4. Feladat:
Egy négyzetes mátrix felső hárömszögében van-e prímszám? (A primszám-ra vonatkozó függvényt nem kell most külön definiálni. Jelölje prim() !)
40
Amennyiben ki szeretnénk használni a felsorolóknak azt a tulajdonságát, hogy a még el nem használt elemekre, folytatni szeretnénk tovább a lineáris keresést, akkor az alábbi módosításokat kell végrehajtani a specifikációban és az algoritmusunkban:
41
1.7 Típus specifikáció Feladat: Racionális számok típusának leírása lét egész hányadosaként. Absztrakt típus Implementáció
Típus értékek
Típus műveletei Műveletek az implementáció szintjén
- hogyan lesz az adott implementáció kivitelezve - invariáns tulajdonság Absztrakt típus: Típus érték: Típus műveletei: +, -, *, / Implementáció szintje:
Típus műveletei:
Műveletek specfikálása:
Megjegyzés: Amennyiben például a deltoid típust szeretnénk leírni, úgy az Absztrakt típus szinten a típus értékhez Deltoid –ot írunk
42
1.8 Szekvenciális inputfile A szekvenciális inputfile nevezetes felsorolója: t.First() : sf, df, f:read t.Next(): sf, df, f:read t.Current(): df t.End(): sd = abnorm Ahol sf a státusza f-nek (norm, abnorm); df a data-ja f-nek; f – file Feladat: Adott egy banki nyilvántartás: - számlaszám - egyenleg egy szekvenciális inputfileban. Írjuk ki egy output fileba azokat, akik tartoznak, és mennyi az összes tartozás összege? -
Két összegzés
Visszavezetés: -
f nevezetes felsorolóját használjuk
-
43
Algoritmus:
Feladat: Adott egy szavakat tartalmazó fájl. Az első ’a’-betűs szó után hány 3 betűs szó van? KERESÉS
t.First()
SZÁMLÁLÁS t.End() Specifikáció:
felsoroló állapota
most elhagyható
Visszavezetés: Keresés: -
f nevezetes felsoroló
Számlálás: -
f nevezetes felsoroló
44
Algoritmus: Keresés: - elem nem kell
A c:=0 mellől a t.First() kimarad, mert folytatni szeretnénk a felsorolást. Feladat: Banki tranzkciók: - ügyfélkód - időpont - összeg A file rendezett ügyfélkód szerint. Adott ügyfél legkisebb betett összegét szeretnénk megtudni. KERESÉS
t.First()
FELT.MIN.KER t.End() A feltételes minimum keresés nem megy végig a fileon! Specifikáció:
első olyan rec, ahol az ügyfél kódja megjelenik
45
Visszavezetés: Keresés: - f nevezetes felsorolója
Felt.min.ker: - f nevezetes felsorolója;
módosítással
Algoritmus:
46
1.9 Egyedi felsorolók készítése Feladat: Adottak lakások adatai: -
kerület azonosító ára terület tulajdonos
Továbbá tudjuk, hogy a file rendezett kerület, azon belül lakás azonosító szerint. Melyik kerületben legdrágább az átlagos négyzetméter ár?
Mivel ezen az állapottéren nem tudjuk megoldani a feladatot, ezért áttérünk egy új állapottére, amire rá tudjuk húzni az egyik programozási tétel sablonunkat. (Állapottér átalakításos feladat)
Írjuk fel a feladat megoldásához szükséges Maximum keresés felsorolókra vonatkozó algoritmusát, a sablon alapján. Most bevezetünk egy selem: keratl típusú segédváltozót. Miután felírtuk az algoritmusunk, külön kell specifikálni a t.First(),t.Next(),t.End() és t.Current() metódusokat.
A t.First(),t.Next(),t.End() és t.Current() metódusok gyakran belső tételek. Mivel sok esetben nehezebb őket specifikálni, ezért először az algoritmusukat rajzoljuk fel, majd csak utána specifikáljuk. t.First( ): Megjegyzés: Maximum keresés tétele miatt f nem üres, ezért sf-et nem fogjuk ellenőrizni. Továbbá legyen : elem – keratl típusú változó.
47
Specifikáció: Megjegyzés:
monoton nővekvő ker szerint,
ezen belül szig. mon nő azonosító szerint.
t.Next( ):
algo. első sora nélkül!
Specifikáció:
t.End( ): Feladat: Karakterekből álló szekvenciális inputfileban hány ’w’ –betűt tartalmazó szó van? Specifikáció:
48
Új állapot tér:
Algoritmus:
t.First() = t.Next( ) Algoritmusa és Specifikációja
Specifikáció:
49
Feladat: Programozási verseny: Adott a fileban: - csapat azonosító - feladat sorszám - beküldési idő (hány perc telt el a kezdettől) Csapat azonosító szerint rendezett a file. Ki nyerte a versenyt? Nyerés: - Az a csapat, aki a legtöbb feladatot megoldotta - Ha két csapat ugyan annyi feladatot oldott meg, akkor a nyertes aki hamarabb oldotta meg Specifikáció:
Eredeti állapot tér:
Ezen az állapot téren nem tudjuk megoldani a feladatot, így átalakítjuk:
Maximum keresés
elem:eredmény típusú változó.
50
t. First( ): 1. csapat feldolgozása
e: eredmény típusú
sf,df a felsoroló folytatása
t. Next( ):
. . lásd Kevés idő miatt elfogadható ez a felírási mód a ZH-n
Itt is lehet ezt a felírást használni! t. End( ): t. Current( ):elem egy példánya Ami még kell: a nagyobb függvény elkészítése!!!
51
1.10 Összefuttató felsoroló -
Adott két rendezett sorozat, elő kell állítani a kimenetet
Az alábbi minta feladatokon keresztül példákat láthatunk a metszet, a szimmetrikus differenciál és a különbség halmazműveletekre vonatkozó feladatokra egy-egy egyszerűbb példát, amilyen a tavalyi zárthelyi dolgozatban is találkozhattunk, továbbá egy nehezebb feladatot (record-os) az unió műveletre. 1. Feladat: Bemenet: két rendezett sorozat - első file tartalmazza az angolul tudó hallgatók neveit - második file tartalmazza a németül tudó hallgatók neveit Kik azok a hallgatók, akik pontosan egy nyelvet beszélnek? (Szimmetrikus differencia)
Specifikáció:
52
Vagyarázatok a pirossal kiemelt részekről: : Általában vagyot használunk, mert mind a két fileunkat teljesen fel szeretnénk dolgozni. Abban az esetben, ha metszetet vagy különbséget kérdeznek a feladatban, akkor jobb az -t használni, mivel ezekben az esetekben ügyesebb és hatékonyabb nem feldolgozni a két fileunkat.
Abban az esetben, ha elfogyott a németesek nevét tartalmazó fileunk vagy van még az angol neveket tartalmazó file-ban valami adatunk és névsor szerint előrébb található az adatban foglalt név, a németeseket tartalmazó file-unkhoz képest, akkor lépünk be ebbe az ágba. Itt da-t dolgozzuk fel és csak az a fileból olvasunk. :
Mind két fileból feldolgozunk, ezért szükséges, hogy mind 2 file tartalmazzon adatot és ha olyan állapotba érünk, hogy az angolos és németes fileban is megtaláljuk ugyan annak a tanulónak a nevét, akkor lépünk be ebbe az elágazásba. Ekkor a feladat miatt (szimmetrikus differencia) a metszet nem számít, így nem iratjuk ki a hallgatót, csak mind két fileból beolvassuk a következő nevet. da > dn: (sa = abnorm) V (sn=norm da > dn) Abban az esetben, ha elfogyott az angolosok nevét tartalmazó fileunk vagy van még a német neveket tartalmazó file-ban valami adatunk és névsor szerint előrébb található az adatban foglalt név, az angolosok nevét tartalmazó file-unkhoz képest, akkor lépünk be ebbe az ágba. Itt dn-t dolgozzuk fel és csak az n fileból olvasunk.
53
2. Feladat: Bemenet: két rendezett sorozat - első file tartalmazza az angolul tudó hallgatók neveit - második file tartalmazza a németül tudó hallgatók neveit Kik azok, akik két nyelven beszélnek? (Metszet)
Specifikáció:
Algoritmus:
54
3. Feladat: Bemenet: két rendezett sorozat - első file tartalmazza az angolul tudó hallgatók neveit - második file tartalmazza a németül tudó hallgatók neveit Kik azok, akik csak angolul beszélnek? (Különbség)
Specifikáció:
Algoritmus:
55
4. Feladat: (Bonyolultabb) – tavalyi zh-ban nem volt ilyen Adott egy zh-kat és egy pót zh-kat tartalmazó file. Az adott fileban: - EHA kód és pont szerepel EHA kód szerint szigorúan monoton növekvő sorrendben Mi lett az adott diák eredménye, ha a jobbik pontszám számít? (Unió) Specifikáció:
1. : A felírás:
- vegyük a hallgatót a zh’-ből Módosítsuk a pont rekordot, a zh’ és pót’ közül a
maximálisra. 2.:
Algoritmus:
56
2. Géptermi gyakorlatok 2.1 Intervallumos programozási tételek megvalóstása
A lentebbi tételekben levő m,n bemeneti paraméterek, melyeket be kell olvasni az inputról. Például: int m,n; cout << ”Adja meg az intervallum also erteket: ” << endl; cin >> m; cout << ”Adja meg az intervallum felso erteket: ” << endl; cin n; Az alábbi kódokat a lefutás érdekében a főprogramba kell írni (main.cpp)main függvény.
Összegzés: #include
using namespace std; int s = 0; for(int i=0; i<=n;++i) { s = s + f(i); // f(i) lehet pl egy tömb (azt is kell deklarálni!) }
Számlálás: #include using namespace std; int c = 0; for(int i=m; i<=n;++i) { if (Betha(i)) // Betha(i) egy feltétel (Boolean értéket ad vissza) { c = c + 1; // vagy ++c; } }
57
Maximum kiválasztás: #include using namespace std; int max = f(m); // Itt f() lehet szintén egy tömb, ekkor dekralárni kell! int ind = m; for(int i=m+1; i<=n;++i) { if (f(i) > max) { max = f(i); ind = i; } }
Kiválasztás: #include using namespace std; int i; for(i=m; !Betha(i);++i); // Betha(i) egy feltétel (Boolean értéket ad vissza)
Lineáris keresés: #include using namespace std; int ind = 0; bool l = false; for(int i=m; !l && i<=n; ++i) { l = Betha(i); ind = i; }
58
Feltételes maximum keresés: #include using namespace std;
int ind = 0; bool l = false; int max = 0; for (int i=m;i<=n;++i) { if(!beta(i)); else if(beta(i) && l) { if(max
59
2.2 Függvények használata C++ programozási nyelvben Bevezető: Hogyan deklarálhatunk C++ nyelven egy függvényt: [VISSZATÉRÉSI ÉRTÉK] [FÜGGVÉNY NEVE]([PARAMÉTEREK]);
Hogyan definiálunk egy függvényt C++ nyelven: [VISSZATÉRÉSI ÉRTÉK] [FÜGGVÉNY NEVE]([PARAMÉTEREK]) { [PARANCSOK]; }
Hogy használhatunk függvényeket a főprogramunkban? (main.cpp) [FÜGGVÉNY DEKLARÁCIÓ] int main() { … //Ide kerülhet a függvényhívás is : [FÜGGVÉNY NEVE]([SZÜKSÉGES PARAMÉTEREK]); } [FÜGGVÉNY DEFINÍCIÓ] Miért jó? Átláthatóbb lesz a program, továbbá a későbbiekben egyszerűbb lesz csomagokra bontani a programunkat. 1. típus: Egyes programozási nyelvekben eljárásként szereplő függvény. Egy olyan függvény, melynek nincsen visszatérési értéke. (void) void Kiír() { std::cout << "Hello World!" << std:endl; }
60
2. típus: Olyan függvények, melyeknek van visszatérési értékük. 1. példa: std::string Kiir() { return "Hello World!"; }
2. példa: int Szam() { int m = 0; return m; }
Arra kell figyelnünk, hogy ha int visszatérési értéket vár a függvényünk, akkor a return kulcsszó után olyan változót kell írnunk, ami megfelelő típusú. Paraméterátadás A C++-ban két féle paraméterátadási mód van. 1. Érték szerinti, az átadott típusból másolat készül a memóriában, az eredeti értéket nem módosítja a függvény. int Osszeg(int a, int b) { return a + b; }
Itt a és b összegével tér vissza a függvény. 2. Cím szerinti, paraméterként az átadott típus referenciája szerepel, a függvény módosíthatja a paramétereket. void Osszeg(int a, int b, int &c) { c = a + b; }
Itt a harmadik paraméter nem "c" értéke, hanem a memóriában elfoglalt címe.
61
Lokális változók A függvények belsejében (illetve a programban lévő blokkokon belül) deklarált változókat lokális változóknak nevezzük. Ez a gyakorlatban azt jelenti, hogy a láthatóságuk és élettartalmuk a függvényen (blokkon) belülre korlátozódik. A lokális változók a függvényhívás végén automatikusan megsemmisülnek és kívülről nem hivatkozhatóak. //Két változó értékének cseréje void swap(int &a, int &b) { int tmp = a; //nem dinamikusan (statikusan) lefoglalt változó a = b; b = tmp; } tmp = 10; //Hiba, tmp nem hivatkozható a hatókörén (a függvény blokkján) kívül
A dinamikus objektumokra mutató pointerek szintén megsemmisülnek a hatókörükből kikerülve, de az objektum maga nem. int * createArray(int n) { int * v = new int [n]; return v; //A függvény egy n elemű tömbre mutató pointerrel tér vissza } int * t = createArray(10); t[0] = 12; //Működik, most t mutat a tömbre v[1] = 2; //Hiba, v már nem létezik
Ha nem gondoskodunk a blokkon belül létrehozott dinamikus objektum külső elérhetőségéről, az érvényes hivatkozás nélkül a memóriában marad, azaz memóriaszivárgás keletkezik. void func() { int * v = new int [10]; } v[0] = 12; /*Hiba, a tömbre mutató pointer már nem létezik, és más sem mutat rá -> memóriaszivárgás*/
62
2.3 Összetettebb feladat megoldása intervallumos programozási tételek segítségével
#include #include #include // fstream kell nekünk, hogy fájlt tudjunk kezelni using namespace std; // továbbfejlesztettük a beolvasás műveletet, hogy ne csak sima cin-ről olvasson be hanem bármilyen ios beviteli cuccról void beolvas(vector &myvector, istream &_stream); // az összegzés helyett produktum int szorzat(const vector &myvector); // Ezt a függvényt adjuk majd át a kereséseknek felételként // eldöntjük, hogy a szám páros-e és logikai értékkel térünk vissza (bool) bool parose(int a); //kiválasztás tétele, vektoron végigmegyünk és kiválasztjuk az első béta feltételnek megfelelő paramétert int kivalaszt(const vector &_vektor, bool beta (int)); // keresés - az átadott vektoron végigmegyünk és visszatérünk vele, hogy találtunk-e béta feltételnek megfelelő értéket, ha igen akkor index változóba írjuk a választ // index változót tudjuk módosítani, mivel & jel előtte van bool kereses(const vector &_vektor, bool beta (int), int &index);
int main () { //létrehozzuk a fájl beolvasására szolgáló változót fstream filestream; filestream.open ("test.txt");// megnyitjuk a fájlt vector myvector; int index; //int myint; beolvas(myvector,filestream); // a módosított beolvas függvénnyel beolvastatjuk a file-ból az adatokat //mivel a keresésnek visszatérési értéke logikai, hogy találtunk-e feltételnek megfelelőt, ezért egy if-be nyugodtan berakhatjuk, mivel az if logikai eredményt vár //a fetltétel függvényt csak egyszerűen átadjuk (zárójel nélkül, mert ha zárójelet írunk akkor az a függvény meghívását jelentené) if ( kereses(myvector,parose,index) ) { //hozzáadunk az index-hez egyet, mert a felhasználó számára nem a 0-tól indexelés a logikus
63
cout << "elemszam: " << (index+1) << endl; } else { cout << "nincs talalat" << endl; } cout << "sorzat: " << szorzat(myvector) << endl; return 0; }
void beolvas(vector &myvector, istream &_stream) { myvector.clear();//kiürítjük a vektort int myint; do { _stream >> myint;// az input stream-ről (fstream vagy cin) beolvasunk egy int-et if(myint!=0)//csak akkor adjuk hozzá a vektorhoz, ha nem 0-t írt be { myvector.push_back (myint); } } while (myint != 0); } int szorzat(const vector &myvector) { int ossz; ossz=1;//összegzés műveletéhez képest itt 1 az alap érték, mivel 0*a = 0 és 1*a=a minden a-ra for (int i=0; i<myvector.size(); ++i) { ossz=ossz*myvector[i]; } return ossz; }
bool parose(int a) { // % a maradékos osztás, és ha elosztunk egy a számot b-vel és a maradék 0 akkor b osztója a-nak return (a%2 == 0); /* if (a%2 == 0) return true; else return false; */ }
64
int kivalaszt(const vector &_vektor, bool beta (int)) { int i = 0; // egy ciklust csinálunk, ami addig megy amíg a feltételnek meg nem felel az aktuális elem while (!beta(_vektor[i])) { // a magban csak léptetünk semmi más dolgunk nincs i++; //i=i+1; } return i; } bool kereses(const vector &_vektor, bool beta (int), int &index) { int i = 0; // szintén ciklussal addig megyünk amíg a feltételnek meg nem felel az aktuális elem // csak hozzátesszük, hogy ha a ciklus végére ér akkor álljon le while (!beta(_vektor[i]) && i<_vektor.size()) { i++; } if (i >= _vektor.size())// ha a ciklus azért állt le, mert a végére értünk akkor nem találtunk feltételnek megfelelő elemet { return false;//tehát hasissal térünk vissza és index-et nem módisítjuk } else // ha viszont megtaláltuk akkor { index = i; // index-ben eltároljuk hogy hol találtuk return true;// igazzal térünk vissza } }
65
2.4 Hibakezelés #include #include using namespace std; enum Hibak{NEGATIV_REKORD,NEGATIV_ERTEK}; void beolvas(vector &pl); int main() { vector T; try { beolvas(T); } catch(Hibak e) { switch(e) { case NEGATIV_REKORD: cout << "A rekordszam negativ!"; break; case NEGATIV_ERTEK: cout << "Az egyik ertek negativ!"; break; } } return 0; } void beolvas(vector &pl) { int n; cin >> n; if(n < 0)throw NEGATIV_REKORD; pl.resize(n); for(int a=0;a> pl[a]; if(pl[a] < 0 || pl[a] > 50)throw NEGATIV_ERTEK; } }
Megjegyzés: Az „enum” olyan adattípust jelöl, melynek lehetséges értékei egy konstanshalmazból kerülnek ki. A fordító balról jobbra haladva nullával kezdve egész értékeket feleltet meg a felsorolt konstansoknak. Ha egy konstansnak egész értéket adunk, akkor a következő elemek ettől a kiindulási értéktől kapnak értéket.
66
2.5 Csomagokra bontás Többek között a programkód átláthatósága és az újra felhasználhatóság érdekében a programjainkat csomagokra bontjuk. A félév során két féle csomag típust használunk. Az egyik csomagtípus az úgynevezett Header file-ok típusa. Ezekben a fileokban elsősorban függvény és típus deklarációkat tárolunk. Leggyakrabban használt kiterjesztése ezen fileoknak a *.h . A második típusú csomagban pedig a függvény definíciókat tároljuk. Ezen file-ok kiterjesztése pedig *.cpp . Első lépésként térjünk el a fentebb megszokott programozási módszertől és a függvények deklarációját tároljuk egy fuggveny.h fileban, míg a maradék kódot hagyjuk benne a main.cpp ben. Ahhoz, hogy leforduljon a megírt prógramunk figyelmeztetnünk kell a fordításért felelős egységet (linker-t) , hogy hol találja meg a deklarációkat. Már korábban is használtunk ilyen „figyelmeztetéseket”, csak akkor a számítógépre már valahova feltelepített könyvtárakat rendeltük hozzá a programunkhoz. Lásd #include . Amennyiben < > között szerepel az elérni kívánt file / könyvtár , úgy a gépen bárhol lehet a könyvtár a linker megkeresi és beteszi. Amennyiben a main.cpp függvénnyel 1 mappába tesszük a header file-ainkat, úgy egy másik módszert használunk az #include ”fuggvenyek.h” kódot. Ekkor a linker a project könyvtárban keresi meg autómatikusan a fileunkat. Visszatérve a feladatunkhoz annyit kell tennünk, hogy a main. cpp –be felülre beírjuk az #include ”fuggvenyek.h” kódot. A fuggvenyek.h fileunkban alábbi sorok láthatóak: #ifndef TETEL_H_INCLUDED #define TETEL_H_INCLUDED . . . #endif // TETEL_H_INCLUDED
Ez a kód segít abban, hogy elkerüljük az összeférhetetlenséget. Második lépésben bontsuk még tovább a programunkat. A fuggveny.h Header fileban tároljuk a függvények deklarációját A fuggvenyek.cpp file-ban a Függvények definícióját A main.cpp file-ban pedig a főprogramunkat Innentől kezdve a már megírt fuggveny.h és fuggveny.cpp –t egyszerűen fel tudjuk használni későbbi projectekben is.
67
2.6 Struktúrák A programozás során gyakran találkozhatunk olyan esetekkel, amikor több különböző adatot egy egységként kell kezelnünk, például egy könyvnek van szerzője, címe, kiadója, stb. A C++ nyelvben a struktúra (struct) több különböző típusú adatok együttese: //a Konyv struktúra definíciója struct Konyv { std::string cim; std::string iro; int ev; };
A "string" típus karaktersorozatot jelöl, és a standard névtérben helyezkedik el. A struktúrák adattagjaira a pont operátor (.) segítségével hivatkozhatunk és adhatunk nekik értéket: //Konyv struktúra megadása tagonként Konyv b; //típusként kezelhetjük – Létrehozunk egy Konyv típust, b neven b.cim = "A C++ programozási nyelv"; b.iro = "Bjarne Stroustrup"; b.ev = 2005; //Konyv kezdőértékadása Book c = {"A C++ programozási nyelv", "Bjarne Stroustrup", 2005}; //A c azonos tartalmú b-vel.
Amennyiben létrehozunk egy ilyen típust írhatunk olyan függvényeket is, melyeknek visszatérési értéke Konyv lesz.
68
2.7 Osztályok A C++ az objektum-orientált programozás megvalósításához egyrészt kibővíti a struktúrákat, másrészt bevezeti a class típust. Mindkettő alkalmas osztály definiálására. Egy osztály (class) adattagjának háromféle elérhetősége lehet: -
public, nyilvános mindenki számára elérhető private, privát csak az osztályon belülről lehet elérni, illetve barát osztályok és függvények protected, védett a származtatott osztályok számára közvetlen elérhetőséget biztosít. A private tagok a leszármazottakból csak az ősosztály tagfüggvényeiből (metódusok) elérhetőek.
A kurzus során csak a public és private elérhetőséget használjuk. //Egy egyszerű osztály class EgyszeruKonyv { public: EgyszeruKonyv (std::string param_cim){ cim = param_cim; } private: std:string cim; };
Konstruktorok: Az objektumok kezdeti értékadásaiért (inicializálás) speciális tagfüggvények a konstruktorok felelnek. A konstruktor olyan tagfüggvény amelynek neve megegyezik az osztályéval és nem rendelkezik típussal. A fordító minden olyan esetben mikor egy objektum létrejön meghívja a konstruktorát. Egy osztálynak bármennyi konstruktora lehet a szignatúrától függően. Alapértelmezés szerint minden osztály két konstruktorral rendelkezik, a paraméter nélküli (default) és a másoló (copy) konstruktorral. Ha saját konstruktort készítünk, attól fogva az alapértelmezett nem lesz elérhető. A konstruktorok egyaránt lehetnek public, private vagy protected elérésűek. A csak private konstruktorokat tartalmazó osztályt rejtett osztálynak nevezzük. Példa default konstruktorra: class Halmaz { public: Halmaz (){ms=10; size=0; tomb = new int[ms];} private: … };
69
Példa copy konstruktorra: class Halmaz { public: Halmaz (const Halmaz& o){…} private: … };
Destruktorok: Az objektumok által felhasznált memória mindaddig lefoglalt marad, míg fel nem szabadítjuk. Erre a célra a C++ biztosít számunkra egy speciális tagfüggvényt, a destruktort. Hasonlóan a konstruktorhoz, a destruktornak sincs visszatérési értéke. A destruktornak nem lehetnek paraméterei. A destruktor nevét az osztály nevéből és a hullám karakterből (tilde: ~) képezzük: class Halmaz { public: Halmaz (){…} // Konstruktor ~Halmaz(){…} // Destruktor private: … };
Ha nem definiálunk destruktort az osztályunkban, a fordító automatikusan létrehozza. A destruktor minden olyan esetben meghívódik amikor az objektum érvényessége megszűnik. Kivételt képeznek a dinamikusan (a new operátorral) létrehozott példányok, amelyeknek csak a megfelelő delete operátor hívhatja meg a destruktorát. A destruktor közvetlenül is hívható. Dinamikus tömbök esetén a konstruktorok az indexek növekvő sorrendjében hívódnak meg, a destruktorok éppen fordítva, de csak a delete[] operátor alkalmazásával. A statikus tömbök ugyanígy törlődnek, de automatikusan (tehát nem kell delete), amint kikerülnek a hatókörükből. A nem megfelelő delete használatával a legjobb esetben is csak a tömb első eleme semmisül meg. Operátorok: A C++-ban nem vezethetünk be új operátorokat, de majdnem mindegyiket túlterhelhetjük. Általában meghatározott számú operandusuk lehet (egy, kettő vagy három), kivéve a függvényhívás operátor (operator()), amelynek bármennyi operandusa lehet. Példa gyakran használt operátorokra: +,-,*,/,&,= stb.
70
class Complex { public: … private: Complex operator+(const Complex& lhs, const Complex& rhs) { return Complex(lhs) += rhs; } };
Operátor túlterhelés: A közönséges függvényekhez hasonlóan a legtöbb operátort is túl lehet terhelni, amely a felhasználói típusok kényelmesebb, szabványosabb használatát teszi lehetővé Például a kiíró operátor (<<) túlterhelése: class Complex { public: friend std::ostream& operator<<(std::ostream& stream, const Complex& z); private: … { return Complex(lhs) += rhs; } }; //az ostream definíciójához nem férünk hozzá, de //operator<<(ostream&, const complex&)-t definiálhatunk std::ostream& operator<<(std::ostream& stream, const Complex& z) { return (stream << '(' << z.re << ", " << z.im << ')'); }
Ezzel például a közönséges szöveg kiírása mellett egy teljesen megírt Complex class esetén egy komplex számot is rögtön ki tudunk írni: //Ezután az operátort egyszerűen használhatjuk: Complex c(1.0, 4.6); std::cout << c; //A kimeneten megjelenik: (1.0, 4.6)
71
Amennyiben csomagokban dolgozunk a classokat egy header file-ban deklaráljuk, míg a classokon belül található metódusokat külön cpp fileban. Ekkor fontos, hogy az adott cpp filehoz hozzá kell rendelnünk a Header filet, továbbá a függvény definíciókban jeleznünk kell, hogy az adott metódusok az osztályhoz tartoznak. Ezt a :: operátorral tudjuk megtenni. Példa: halmaz.h #ifndef HALMAZ_H_INCLUDED #define HALMAZ_H_INCLUDED … class Halmaz { public: Halmaz();// Konstruktor deklarációja ~Halmaz();// Destruktor deklarációja private: … }; #endif // HALMAZ_H_INCLUDED
halmaz.cpp #include #include ”halmaz.h” Halmaz::Halmaz() // Konstruktor definíciója { … } Halmaz::~Halmaz() // Destruktor definíciója { … }
72
2.8 A Deltoid osztály megvalósítása Megjegyzés: Programozás beadandó feladat. Ne másold le, mert sikertelenül teljesíted a kurzust! A kód néhány helyen megtört a dokumentumban. Próba esetén figyelj rá! Feladat 2. beadandó (A szakirány) - 8. feladat Valósítsa meg a deltoidok típusát! egy deltoidnak le lehessen kérdezni az oldalainak hosszát, a szögeit, átlóinak hosszát, a területét, a kerületét. Reprezentálja a deltoidokat az átlójuk hosszával és azzal az aránnyal, ami megmutatja, hogy hol metszi az egyik átló a másikat! A főprogram adjon lehetőséget a típus műveleteinek kipróbálására! Egy menű segítségével lehessenegy deltoidot megadni, majd annak paramétereit megtekinteni.
73
Deltoid típus Absztrakt típus: -
Típus érték: Deltoid Típus műveletek: * (nagyítás), / (kicsinyítés), == (egybevágóság vizsgálata) Műveletek és egyes metódusok specifikálása:
*:
/:
==:
=
terület(): kerület():
oldalak() és
atlok(): szogek():
oldalak():
Implementációs szint:
74
Implementáció Deltoid osztálya: A Deltoid típus megvalósításánál kihasználjuk a C++ nyelv azon lehetőségét, hogy az operátorokat felül lehet definiálni, és ennél fogva lehetőségünk nyílik egy teljes deltoidot kiiratni továbbá egyéb műveleteket végezni egy adott deltoiddal. deltoid.h class Deltoid { private: float e; // A deltoid egyik átlója float f; // A deltoid másik átlója float arany; // A két átló metszéspontjának aránya public: enum Hibak{E_ATLO_ERROR, F_ATLO_ERROR, ARANY_ERROR, REL_EF_ERROR, OP_N_ERROR, OP_K_ERROR}; Deltoid(); // Konstruktor Deltoid(float e0, float f0, float arany0); void setE(float tmp); void setF(float tmp); void setArany(float tmp); Oldalak oldalak(); // Meghatározza adott adatokból a deltoid oldalait Atlo atlok(); // Meghatározza adott adatokból a deltoid átlóinak a hosszát Szogek szogek(); // Meghatározza adott adatokból a deltoid szögeit float kerulet(); // Meghatározza adott adatokból a deltoid kerületét float terulet(); // Meghatározza adott adatokból a deltoid Területét friend Deltoid read_from_console(); friend std::istream& operator>> (std::istream& inp, Deltoid& b); // Beolvasó operátor friend std::ostream& operator<< (std::ostream& oup, const Deltoid& b); // Kiíró operátor friend bool operator== (const Deltoid& a, const Deltoid& b); // Egybevágósági vizsgálat Deltoid operator* (float konst); // Egy deltoid nagyítása Deltoid operator/ (float konst); // Egy deltoid kicsinyítése ~Deltoid(); // Destruktor };
75
deltoid.cpp #include #include <math.h> #include "deltoid.h" #define PI 3.14159265 using namespace std; Deltoid::Deltoid() { e = 0; f = 0; arany = 0; } Deltoid::Deltoid(float e0, float f0, float arany0) { if (e0 <= 0){throw E_ATLO_ERROR;} if (f0 <= 0){throw F_ATLO_ERROR;} if (arany0 < 0 || arany0 > 1){throw ARANY_ERROR;} e = e0; f = f0; arany = arany0; } void Deltoid::setE(float tmp) { if (tmp > 0) { e = tmp; } } void Deltoid::setF(float tmp) { if (tmp > 0) { f = tmp; } } void Deltoid::setArany(float tmp) { if (tmp >= 0 || tmp <= 1) { arany = tmp; } }
76
Oldalak Deltoid::oldalak() { Oldalak del; float tmp1 = (f*arany); float tmp2 = (f - tmp1); float tmp3 = (e / 2); del.a = sqrt(pow(tmp1, 2) + pow(tmp3, 2)); del.b = sqrt(pow(tmp2, 2) + pow(tmp3, 2)); return del; } Atlo Deltoid::atlok() { Atlo delto; delto.ee = e; delto.ff = f; return delto; } Szogek Deltoid::szogek() { Szogek delt; float tmp1 = (f*arany); float tmp2 = (f - tmp1); float tmp3 = (e / 2); float sztmp1 = tmp1/tmp3; delt.alfa = atan(sztmp1) * (360 / PI); delt.gamma = delt.alfa; float sztmp2 = tmp3/tmp1; delt.beta = atan(sztmp2) * (360 / PI); float sztmp3 = tmp3/tmp2; delt.delta = atan(sztmp3) * (360 / PI); return delt; } float Deltoid::kerulet() { Oldalak del; float tmp1 = (f*arany); float tmp2 = (f - tmp1); float tmp3 = (e / 2); del.a = sqrt(pow(tmp1, 2) + pow(tmp3, 2)); del.b = sqrt(pow(tmp2, 2) + pow(tmp3, 2)); float k = 2 * (del.a + del.b); return k; }
77
float Deltoid::terulet() { return ((e*f)/2); } Deltoid Deltoid::operator* (float konst) { if (konst < 1){throw Deltoid::OP_N_ERROR;} float e2 = e * konst; float f2 = f * konst; float arany2 = arany; Deltoid szr1(e2, f2, arany2); return szr1; } Deltoid Deltoid::operator/ (float konst) { if(konst <= 0 || konst > 1){throw Deltoid::OP_K_ERROR;} float e2 = e * konst; float f2 = f * konst; float arany2 = arany; Deltoid szr1(e2, f2, arany2); return szr1; } istream& operator>> (istream&inp, Deltoid& b) { inp >> b.e; inp >> b.f; inp >> b.arany; return inp; } ostream& operator<< (ostream& oup, const Deltoid& b) { oup << "("; oup << b.e; oup << ", "; oup << b.f; oup << ", "; oup << b.arany; oup << ")"; return oup; }
78
bool operator== (const Deltoid& a, const Deltoid& b) { if(&a == &b) { return true; } float x1 = a.arany; float x2 = b.arany; float tmp = x1 + x2; bool egybevago = false; if(a.e==b.e) { if(a.f==b.f) { egybevago = (tmp == 1); } } return egybevago; } Deltoid::~Deltoid() { } read.h #ifndef READ_H_INCLUDED #define READ_H_INCLUDED #include #include "deltoid.h" Deltoid read_from_console(); // Konzolos beolvasás #endif // READ_H_INCLUDED read.cpp #include #include "read.h" #include "deltoid.h" using namespace std; Deltoid read_from_console() { cout << "Kérem adja meg a deltoid adatait az alábbi módon: e f arány " << endl; cout << "(e - a deltoid rövidebbik átlója" << endl; cout << "f a deltoid hosszabbik átlója" << endl; cout << "arány - a két átló metszéspontjának az aránya (0-1 közötti érték))" << endl; Deltoid d1; cout << "A deltoid: " << endl; cin >> d1; if (d1.e <= 0){throw Deltoid::E_ATLO_ERROR;} if (d1.f <= 0){throw Deltoid::F_ATLO_ERROR;} if (d1.f < d1.e){throw Deltoid::REL_EF_ERROR;} if (d1.arany < 0 || d1.arany > 1){throw Deltoid::ARANY_ERROR;} return d1; }
79
main.cpp /* Név: Nagy Krisztián Kurzus: Programozás Beadandó: 2. beadandó (A szakirány) - 8. feladat Feladat leírása --------------Valósítsa meg a deltoidok típusát! egy deltoidnak le lehessen kérdezni az oldalainak hosszát, a szögeit, átlóinak hosszát, a területét, a kerületét. Reprezentálja a deltoidokat az átlójuk hosszával és azzal az aránnyal, ami megmutatja, hogy hol metszi az egyik átló a másikat! A főprogram adjon lehetőséget a típus műveleteinek kipróbálására! Egy menű segítségével lehessen egy deltoidot megadni, majd annak paramétereit megtekinteni. */ #include #include "read.h" #include "deltoid.h" using namespace std; int main() { try { cout << "Deltoid 1.0 - Készítette: Nagy Krisztián " << endl; cout << endl; bool ujra = true; do { int valasztas; do { cout << "Adja meg a választott opció számát:" << endl; cout << "1-es Deltoid oldalainak hossza" << endl; cout << "2-es Deltoid átlóinak hossza" << endl; cout << "3-as Deltoid szögei" << endl; cout << "4-es Deltoid kerülete" << endl; cout << "5-ös Deltoid területe" << endl; cout << "6-os Deltoid nagyítása/kicsinyítése" << endl; cout << "7-es Deltoidok egybevágóságának vizsgálata" << endl; cout << "8-as 1-5-ig Teljes elemzés" << endl; cout << "---------------------------" << endl; cout << "9-es Operátor teszt" << endl; cout << endl; cout << "A választott opció: "; cin >> valasztas; cout << endl;
80
switch (valasztas) { case 1: { ujra = true; Deltoid d1 = read_from_console(); Oldalak deo = d1.oldalak(); cout << endl; cout << "A " << d1 << " deltoid oldalainak hossza:" <<" egység és "<< deo.b << " egység." << endl; d1.~Deltoid(); }break; case 2: { ujra = true; Deltoid d1 = read_from_console();
<< deo.a
Atlo dea = d1.atlok(); cout << endl; cout << "A " << d1 <<" deltoid átlóinak hossza:e=" << dea.ee << " egység és f = " << dea.ff << " egység." << endl; d1.~Deltoid(); }break; case 3: { ujra = true; Deltoid d1 = read_from_console(); Szogek dsz = d1.szogek(); cout << endl; cout << "A " << d1 <<" deltoid szögei fokokban: alfa: " << dsz.alfa << " béta: "<< dsz.beta << " gamma: " << dsz.gamma << " delta: " << dsz.delta << endl; d1.~Deltoid(); }break; case 4: { ujra = true; Deltoid d1 = read_from_console(); cout << endl; cout << "A " << d1 <<" deltoid kerülete: " << d1.kerulet() << endl; d1.~Deltoid(); }break; case 5: { ujra = true; Deltoid d1=read_from_console(); cout << endl; cout << "A " << d1 <<" deltoid területe: " << d1.terulet() << endl; d1.~Deltoid(); }break; case 6: { ujra = true; cout << "Deltoidok nagyítása/kicsinyítése" << endl;
81
cout << "Használati útmutató:" << endl; cout << "Nagyításhoz használja írjon be egy 1-nél nagyobb vagy egyenlő értéket." << endl; cout << "Kicsinyítéshez írjon be egy 0-nál nagyobb és 1-nél kisebb vagy egyenlő értéket." << endl; cout << endl; float xdi; Deltoid d1 = read_from_console(); cout << "Adja meg a nagyítás/kicsinyítés mértékét: "; cin >> xdi; if(xdi < 1) { cout << "A " << d1 << " deltoid kicsinyítettje: " << d1/xdi << endl;; } else if (xdi == 1) { cout << "A " << d1 << " deltoid nem változott." << endl; } else { cout << "A " << d1 << " deltoid nagyítottja: " << d1*xdi << endl; } }break; case 7: { cout << "Deltoidok egybevágóságának vizsgálata" << endl; cout << endl; ujra = true; cout << "Első deltoid: " << endl << endl; Deltoid d1 = read_from_console(); cout << "Második deltoid: " << endl << endl; Deltoid d2 = read_from_console(); if(d1 == d2) { cout << "A két deltoid egybevágó" << endl; } else { cout << "A két deltoid nem egybevágó" << endl; } d1.~Deltoid(); d2.~Deltoid(); }break; case 8: { ujra = true; cout << "Deltoid teljes elemzése" << endl << endl; Deltoid d1 = read_from_console(); Oldalak deo = d1.oldalak(); Atlo dea = d1.atlok(); Szogek dsz = d1.szogek(); cout cout cout "<< deo.b << " egység." << endl; cout és f = " << dea.ff << " egység."
<< endl; << "A " << d1 << " deltoid:" << endl; << "- oldalainak hossza: " << deo.a <<" egység és << "- átlóinak hossza: e = " << dea.ee << " egység << endl;
82
cout béta: "<< dsz.beta << " gamma: " cout cout
<< << << <<
"- szögei fokokban: alfa: " << dsz.alfa << " dsz.gamma << " delta: " << dsz.delta << endl; "- területe: " << d1.terulet() << endl; "- kerülete: " << d1.kerulet() << endl;
d1.~Deltoid(); }break; case 9: { cout << cout << cout << Deltoid Deltoid
"Operátor tesztelő zóna" << endl; "----------------------" << endl; "funkciók tesztje" << endl; d1; d2(2, 2, 0.5);
cout << "--Beolvasó operátor(>>) teszt--" << endl; cout << "Írjon be egy deltoidot az alábbi formában: e f arány" << endl; cin >> d1; cout << endl; cout << "--Kiíró operátor(<<) teszt--" << endl; cout << "Fentebb megadott deltoid kiírva: " << d1; cout << endl; cout << "--operator* teszt (nagyítás)--" << endl; cout << "- a deltoid 2x-es nagyítással: " << d1*2 << endl; cout << "- a deltoid 3x-os nagyítással: " << d1*3 << endl; cout << endl; cout << "--operator/ teszt (kicsinyítés)--" << endl; cout << "- a deltoid 0.2-esre kicsinyítés: " << d1/0.2 << endl; cout << "- a deltoid 0.5-ösre kicsinyítés: " << d1/0.5 << endl; cout cout cout cout
<< << << <<
endl; "--operator== teszt (egybevégóság)--" << endl; "1 - egybevágó, 0 - nem egybevágó" << endl; "- a beírt deltoid önmagával: " << (d1==d1) <<
endl; cout << "- a beírt deltoid a (2, 2, 0.5) deltoiddal: " << (d1==d2) << endl; cout << endl; cout << "A TESZT VÉGET ÉRT!" << endl; d1.~Deltoid(); d2.~Deltoid(); }break; } }while(valasztas <1 || valasztas > 9 || cin.fail()); char re; cout << endl; cout << "Szeretné újra futtatni? (i/n)" << endl; cin >> re; cout << endl; if(re == 'n' || re == 'N') { ujra = false; } }while (ujra == true); } catch (Deltoid::Hibak err) { switch(err) {
83
case Deltoid::E_ATLO_ERROR: cout << "Hibás az e átló" << endl;break; case Deltoid::F_ATLO_ERROR: cout << "Hibás az f átló" << endl;break; case Deltoid::REL_EF_ERROR: cout << "Hibás az átlók megadása. Legyen (e < f)!" << endl;break; case Deltoid::ARANY_ERROR: cout << "Hibásan adta meg az arányt" << endl;break; case Deltoid::OP_K_ERROR: cout << "A kicsinyítéshez csak 0-nál nagyobb és 1-nél kisebb vagy egyenlő szám az elfogadott!"<< endl;break; case Deltoid::OP_N_ERROR: cout << "A nagyításhoz csak 1 vagy annál nagyobb szám az elfogadott!"<< endl;break; } } return 0; }
84
2.9 Szekvenciális I/O (Minta feladat) nyilvki.h #ifndef NYILVKI_H_INCLUDED #define NYILVKI_H_INCLUDED #include "cica.h" #include class Nyilvki { std::ofstream f; public: Nyilvki(std::string fnev); void vrajt(Cica &df); ~Nyilvki(); }; #endif // NYILVKI_H_INCLUDED
nyilvki.cpp #include "nyilvki.h" #include <string> using namespace std; Nyilvki::Nyilvki(std::string fnev) { f.open(fnev.c_str()); } void Nyilvki::vrajt(Cica &df) { f << df; } Nyilvki::~Nyilvki() { f.close(); }
nyilv.h #ifndef NYILV_H_INCLUDED #define NYILV_H_INCLUDED #include "cica.h" #include enum Status{NORM, ABNORM}; class Nyilv { std::ifstream f; public: Nyilv(std::string fnev); void reed(Status &sf, Cica &df); ~Nyilv(); }; #endif // NYILV_H_INCLUDED
85
nyilv.cpp #include "nyilv.h" #include <string> using namespace std; Nyilv::Nyilv(std::string fnev) { f.open(fnev.c_str()); } void Nyilv::reed(Status &sf, Cica &df) { f >> df.nev; string faja; f >> faja; if(faja=="SZIAMI") { df.faj=Cica::SZIAMI; } else if(faja=="HAZI") { df.faj=Cica::HAZI; } else if(faja=="PERZSA") { df.faj=Cica::PERZSA; } f >> df.kor; f >> df.suly; if(f.eof()) { sf=ABNORM; } else { sf=NORM; } } Nyilv::~Nyilv() { f.close(); }
86
main.cpp #include #include #include #include
"nyilv.h" "nyilvki.h"
using namespace std;
int main() { Nyilv macskak("input.txt"); Nyilvki macski("output.txt"); Nyilvki macski2("output2.txt"); Status sf=NORM; Cica df; while(sf!=ABNORM) { macskak.reed(sf, df); if( df.pontoz() >3) { macski.vrajt(df); } else { macski2.vrajt(df); } } return 0; }
cica.h #ifndef CICA_H_INCLUDED #define CICA_H_INCLUDED #include struct Cica { enum Faj{SZIAMI, PERZSA, HAZI}; std::string nev; Faj faj; int kor; double suly; int pontoz(); }; std::ostream& operator<<(std::ostream&, const Cica&); #endif // CICA_H_INCLUDED
87
cica.cpp #include "cica.h" using namespace std; int Cica::pontoz() { return (100-kor)*suly*(faj==HAZI ? 0.6 : 1.1)/100 ; } ostream& operator<<(ostream& o, const Cica& c) { o << c.nev << ' '; if(c.faj==Cica::SZIAMI) { o << "SZIAMI"; } else if(c.faj==Cica::HAZI) { o << "HAZI"; } else if(c.faj==Cica::PERZSA) { o << "PERZSA"; } o << ' ' << c.kor << ' ' << c.suly << '\n'; }
TESZT input.txt Miajuu SZIAMI 20 10 Ciccmiricc HAZI 1 2 Tesztmacska PERZSA 5 110 Szemi HAZI 8 11 Megegy PERZSA 73 10 output.txt Miajuu SZIAMI 20 10 Tesztmacska PERZSA 5 110 Szemi HAZI 8 11 output2.txt Ciccmiricc HAZI 1 2 Megegy PERZSA 73 10
88
2.10 Összefuttató felsorolók megvalósítása (Sablon) kozos.h #ifndef KOZOS_H_ #define KOZOS_H_ enum Status{ABNORM,NORM}; #endif
main.h #include #include #include <string> #include "szekin.h" #include "szekout.h" using namespace std; /* A rekord formátuma [név] [adatokszáma] [1. adat] ... [n. adat] ha a több input fájlban azonos névvel rendelkező rekord van, akkor azok közül a nagyobb értékkel rendelkező rekord fog bekerülni a kimenetbe */ int main() { SzekIn in1("in1.txt"); SzekIn in2("in2.txt"); SzekOut out("out.txt"); // Nagy Krisztián oldalán megtaláljátok a táblás anyagot ahol az összefuttatások algoritmusai fent vannak // http://people.inf.elte.hu/naksabi/tanulmanyi.html 12. óra pdfjét nézzétek // // 3 algoritmus van: // * únió - itt minden esetben kiírjuk a beolvasott rekordot, csak ha mindkettőben benne van akkor abban az esetben még valamilyen érték szerint elágazunk // * szimmetrikus differencia - ebben az esetben a középső ág ahol az únióban az összehasonlítás történt, nem csinálunk semmit csak léptetjük az inputokat // * különbség - ebben az esetben meg csak az első ágban írunk ki az outputra, mivel a különbség az ami az elsőben benne van, de a 2.ban nincs // // A 3 algoritmus kb teljesen ugyanaz, csak más esetekben írunk az outputra // A lényeg az, hogy mindig azzal lépünk ami hátrébb van (azonosító szerint, pl.: ha az első fájl 41-en áll a 2. meg 45-n, akkor az 1-t léptetjük) // ha pedig mindkettő ugyanazon áll akkor algoritmusonként mást teszünk
////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////ÚNIÓ //////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// while(in1.getStatus() == NORM || in2.getStatus() == NORM) { cout << in1.current() << endl << in2.current() << endl; cout << "------------------" << endl; ///1. ág - ez az ág akkor fut le amikor az elsőt kell kiiratni az outputra if( in2.getStatus() == ABNORM || // ha már csak az első maradt(2. fájl már abnorm), akkor csak ki kell írni a rekordokat (in1.getStatus() == NORM && in1.current() < in2.current()) // ha mindkettő norm és az második fájl tart előrébb ) { cout << "1. ag nyert" << endl; out.write(in1.current());// akkor az első fájlból a rekordot kiírjuk szimplán in1.next(); // és léptetjük az inputot } // középső ág - ez az ág akkor fut le amikor a mind a két fájl azonos azonosítójú rekordon áll else if(in1.getStatus() == NORM && in2.getStatus() == NORM && //ha mindkettő fájl még megy in1.current() == in2.current()) // és azonos rekord azonosítóról van szó { cout << "kozepso ag nyert" << endl; if(in1.current().getErtek() > in2.current().getErtek())//akkor a nagyobb értékkel rendelkezőt írjuk ki { out.write(in1.current()); } else { out.write(in2.current()); } in1.next();// és léptetjük mind a két inputot in2.next(); }
89
// 2. ág - ebben az esetben a 2. fájlból írjuk ki a rekordot else //a stukiban lévő ifek mindig teljesek, szóval itt esetünkben elég else-t írni { cout << "2. ag nyert" << endl; out.write(in2.current()); in2.next(); } cout << "=================" << endl; }
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////SZIMMETRIKUS DIFFERENCIA /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// /*while(in1.getStatus() == NORM || in2.getStatus() == NORM) { cout << in1.current() << endl << in2.current() << endl; cout << "------------------" << endl; if( in2.getStatus() == ABNORM || // ha már csak az első maradt(2. fájl már abnorm), akkor csak ki kell írni a rekordokat (in1.getStatus() == NORM && in1.current() < in2.current()) // ha mindkettő norm és az második fájl tart előrébb ) { cout << "1. ag nyert" << endl; out.write(in1.current());// akkor az első fájlból a rekordot kiírjuk szimplán in1.next(); // és léptetjük az inputot } else if(in1.getStatus() == NORM && in2.getStatus() == NORM && //ha mindkettő fájl még megy in1.current() == in2.current()) // és azonos rekord azonosítóról van szó { cout << "kozepso ag nyert" << endl; //ebben az esetben nem írunk ki semmit, mivel a szimmetrikus differenciában az únió középső része nem kell in1.next();// és léptetjük mind a két inputot in2.next(); } else //a stukiban lévő ifek mindig teljesek, szóval itt esetünkben elég else-t írni { cout << "2. ag nyert" << endl; out.write(in2.current()); in2.next(); } cout << "=================" << endl; }*/ ////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////KÜLÖNBSÉG /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////// /*while(in1.getStatus() == NORM) { cout << in1.current() << endl << in2.current() << endl; cout << "------------------" << endl; if( in2.getStatus() == ABNORM || // ha már csak az első maradt(2. fájl már abnorm), akkor csak ki kell írni a rekordokat (in1.getStatus() == NORM && in1.current() < in2.current()) // ha mindkettő norm és az második fájl tart előrébb ) { cout << "1. ag nyert" << endl; out.write(in1.current());// akkor az első fájlból a rekordot kiírjuk szimplán in1.next(); // és léptetjük az inputot } else if(in1.getStatus() == NORM && in2.getStatus() == NORM && //ha mindkettő fájl még megy in1.current() == in2.current()) // és azonos rekord azonosítóról van szó { cout << "kozepso ag nyert" << endl; //ebben az esetben nem írunk ki semmit, mivel a szimmetrikus differenciában az únió középső része nem kell in1.next();// és léptetjük mind a két inputot in2.next(); } else //a stukiban lévő ifek mindig teljesek, szóval itt esetünkben elég else-t írni { cout << "2. ag nyert" << endl; //ebben az esetben in2.next(); } cout << "=================" << endl; } */ }
90
rekord.cpp #include "rekord.h" using namespace std; string Rekord::getNev() { return nev; } int Rekord::getErtek() { int sum = 0; for(int a=0;a>(istream& o, Rekord &r) { o >> r.nev; if(o.fail()) return o; int size; o >> size; r.adatok.resize(size); for(int a=0;a<size;++a) { o >> r.adatok[a]; } return o; }
91
rekord.h #ifndef REKORD_H_ #define REKORD_H_ #include #include class Rekord { private: std::string nev;// ez azonosítja egyértelműen az adott rekordot std::vector adatok; public: // visszaadja a rekord nevét vagy azonosítóját std::string getNev(); int getErtek(); //kiemeljük a név szerinti összehasnonlításokat, hogy szebb legyen a main kódja bool operator<(const Rekord& r); bool operator==(const Rekord& r); //barát operátorok a kiiratásra és a beolvasásra friend std::ostream& operator<<(std::ostream& o, const Rekord &r); friend std::istream& operator>>(std::istream& o, Rekord &r); }; /* std::ostream& operator<<(std::ostream& o, const Rekord &r); std::istream& operator>>(std::istream& o, Rekord &r); */ #endif
92
szekin.cpp include "szekin.h" using namespace std; SzekIn::SzekIn(std::string fnev) { f.open(fnev.c_str()); next();// a norm és abnormra állást a next elintézi } SzekIn::~SzekIn() { f.close(); } void SzekIn::next() { f >> df;//beolvassuk a következő rekordot if(f.fail()) { //tesztelésre: cout << "Sikertelen beolvasás" << endl; sf = ABNORM;//megfelelőre állítjuk a státuszt } else { //tesztelésre: cout << df << endl; sf = NORM; } } //Szimplán visszaadjuk a rekordot Rekord SzekIn::current() { return df; } //Szimplán visszaadjuk a státuszt Status SzekIn::getStatus() { return sf; }
szekin.h #ifndef SZEKIN_H_ #define SZEKIN_H_ #include #include #include #include
"kozos.h" "rekord.h"
class SzekIn { private:// privát kell legyen, mert a main nem tudhatja pl, hogy mi a státuszunk -> nem állíthatja át // a fájl maga std::ifstream f; // itt tároljuk, hogy a szekvenciális input norm vagy abnorm Status sf; // az aktuális rekord amin állunk Rekord df; public: //konstruktor (azonos a neve az osztályal és nem kell neki visszatérési értékként semmit írni) // megnyitja a fájlt és egyben rá is lép az első rekordra SzekIn(std::string fnev); //destruktor (paraméter nélkül!! - ez fut le a blokk végén ahol létrehoztuk az adott változót)
93
~SzekIn(); //rálépünk a következő rekordra void next(); //visszaadja az aktuális rekordot Rekord current(); //visszaadjuk a státuszt Status getStatus(); }; #endif
szekout.cpp #include "szekout.h" using namespace std; SzekOut::SzekOut(string fnev) { f.open(fnev.c_str()); } SzekOut::~SzekOut() { f.close(); } void SzekOut::write(const Rekord &r) { // kiírunk egy rekordot és rakunk utána egy sort, mivel a sima kiíró az nem fog rakni utána f << r << endl; }
szekout.h #ifndef SZEKOUT_H_ #define SZEKOUT_H_ #include #include #include "rekord.h" class SzekOut { private: // a fájl maga std::ofstream f; public: SzekOut(std::string fnev); ~SzekOut(); // itt csak kiíró kell, de az vár egy rekordot amit ki kell írnia void write(const Rekord &r); }; #endif
94
MINTA INPUT: in1.txt : ABC 2 1 5 BDF 4 1 2 1 1 DDD 1 1 EEE 5 1 2 4 5 6 GGG 1 1 in2.txt : ABC 2 2 5 DDD 1 54 EEE 2 1 1 FFF 2 5 76 OUTPUT (EREDMÉNY) out.txt BDF 4 1 2 1 1 FFF 2 5 76
95
3. Segédanyagok
Gregorics Tibor weboldala: http://people.inf.elte.hu/gt/ Veszprémi Anna weboldala: http://people.inf.elte.hu/veanna/ Hudoba Péter weboldala: http://hudi89.web.elte.hu/ Hasznos lehet: Giachetta Roberto weboldala: http://people.inf.elte.hu/groberto/ Nagy Krisztián weboldala: http://people.inf.elte.hu/naksabi/ Ajánlott irodalom: Gregorics Tibor - Programozás – Tervezés c. könyve Gregorics Tibor - Programozás – Megvalósítás c. könyve Bjarne Stroustrup – A C++ programozási nyelv Egyéb lehetőségek: Google Wikipedia - http://hu.wikipedia.org/wiki/C%2B%2B
96
4. Felhasznált irodalom (Források) Veszprémi Anna táblás gyakorlatai Hudoba Péter géptermi gyakorlatai Gregorics Tibor weboldala: http://people.inf.elte.hu/gt/ Hudoba Péter weboldala: http://hudi89.web.elte.hu/ Gregorics Tibor - Programozás – Tervezés c. könyve Gregorics Tibor - Programozás – Megvalósítás c. könyve Bjarne Stroustrup – A C++ programozási nyelv Wikipedia - http://hu.wikipedia.org/wiki/C%2B%2B Hudoba Péter órai programjai: Lásd: 2.4 , 2.9, 2.10 fejezet
97