BEVEZETÉS A C# PROGRAMOZÁSBA KÉSZÍTETTE: CZIGLÉCZKY GÁBOR
|
|
- Rezső Dániel Szekeres
- 6 évvel ezelőtt
- Látták:
Átírás
1 BEVEZETÉS A C# PROGRAMOZÁSBA KÉSZÍTETTE: CZIGLÉCZKY GÁBOR
2
3 BEVEZETÉS Napjainkban a vizuális fejlesztőkörnyezetek virágkorukat élik, a programozók jelentős része integrált fejlesztőkörnyezetekben (IDE = Integrated Development Environment) írja programjait. Éppen ezért nagyon fontos, hogy már a középiskolában megismerkedjetek legalább egy ilyen programfejlesztő eszközzel. Ez a tananyag megismertet a Microsoft ingyenesen letölthető és használható Visual Studio Express 2008 programcsomagjával, azon belül pedig a Visual C# Express 2008-as verziójával. A C# egy rendkívül hatékony, aránylag könnyen tanulható programozási nyelv, amellyel egyszerűen és gyorsan tudunk Windows alatt futó programokat, vagy akár webes alkalmazásokat is fejleszteni. Fontos tudni azonban, hogy ezt a tananyagot olyanoknak ajánlom, akik már programoztak valamely programozási nyelven. Az sem jelent hátrányt, ha már találkoztál vizuális fejlesztőkörnyezettel. Cél A Visual C# 2008 kezelőfelületének és alapvető komponenseinek megismerése. Egyszerűbb Windows Forms alkalmazások fejlesztése. A C# nyelvi elemeinek megismerése. Hibajelenségek észlelése, elhárításuk, bolondbiztos programok készítése. A tanult ismeretek alkalmazása egy kisebb csoportban elkészített projektfeladaton keresztül, az együttműködés fejlesztése, a csoportos szoftverfejlesztés kipróbálása. Az elkészült produktumok értékelése különféle szempontok szerint. Követelmény Tudd feltelepíteni, és használatba venni a Visual C# 2008 környezetet. Tudj megtervezni és elkészíteni egy Windows Forms alkalmazást, felhasználói felületével és a mögötte levő kóddal együtt. Ismerd az alapvető komponensek használatát, valamint a C# nyelv legfontosabb elemeit. Legyél képes a programodban a hibákat megtalálni, és azokat kijavítani a hibakereső eszközök segítségével. Legyél képes kis csapatban a társaiddal együttműködve megtervezni, majd elkészíteni egy programot, ezt követően pedig a többieknek bemutatni azt. 3
4 Jelmagyarázat A tanulói jegyzetben a tananyag fontos elemeit, a példákat és a tanulási tippeket különböző ikonok jelölik. Ikon Jelentés A fejezet célmeghatározása. Figyelmesen olvasd el, így megismered a fejezet fókuszpontjait. Az ikon fontos, jól megjegyzendő, megtanulandó ismereteket jelez. Az ikon mellett olyan gondolatébresztő, kérdéseket, felvetéseket, problémákat találsz, amelyek megválaszolásával elmélyülhetsz a témában. Az ismeretek elsajátítását megkönnyítik a példák. Az ikon mellett érdekességeket, példákat, gyakorlati életből vett esetleírásokat találsz. Az ikon a házi feladatot, otthoni munkát jelöli. 4
5 A VISUAL C# EXPRESS 2008 BESZERZÉSE, TELEPÍTÉSE, FELFEDEZÉSE Ennek a témakörnek az a célja, hogy az iskolai vagy az otthoni számítógépen üzembe tudd helyezni a Microsoft Visual C# 2008-as verzióját, képes legyél eligazodni a fejlesztői környezetben, saját igényeid szerint testre szabni, és segítséget kérni a Súgótól. ELMÉLETI ÖSSZEFOGLALÓ A Microsoft.NET felépítése: CLR (Common Language Runtime): 1.1. ábra A Microsoft.NET felépítése Platformfüggetlenség: a programok fejlesztése független az operációs rendszertől és az azt futtató processzortól. Virtuális gépi kód: a fordító nem processzorfüggő gépi kódra fordít, hanem egy képzeletbeli processzor gépi kódjára. A.NET Framework részét képező futtatókörnyezet: a virtuális gépi kódot a program futása közben fordítja le a konkrét fizikai processzor gépi kódjára. 5
6 BCL (Base Class Library):.NET programozók aranybányája Egységes felépítésű osztálykönyvtár: a fejlesztés során használható osztályok, függvények gyűjteményét tartalmazza. Használható: dátumok kezeléséhez, fájlok olvasásához, írásához, adattípusok megvalósításához (pl. sor, verem, lista), adatbázis-kezeléshez. Mindegyik.NET nyelvben használható Strukturált, áttekinthető felépítésű ADO.NET: adatbázisok kezelése (SQL Server, Access, MySQL, Oracle), adatelérésre és adatmegjelenítésre használható komponensek (pl. DataGridView) XML (extensible Markup Language): szabványos adatleíró nyelv, amellyel lehetővé válik XML adatforrások feldolgozása beépített komponensek segítségével ASP.NET: Webes felületű (böngészőben futó) alkalmazásokhoz Windows Forms: Windows alatt futó, grafikus felületű, Windows-vezérlőelemeket használó alkalmazásokhoz Apróbb különbségeket leszámítva ugyanaz a fejlesztés módja mindkét környezetben! CLS (Common Language Specification): A programozási nyelvek közti különbségek (egy részének) eltüntetése Egységesebb szabályok a.net programnyelveiben Nyelvfüggetlenség: Egységes alaptípusok (egész, valós, karakter) Szigorú szabályok például a változók elnevezésére, hatókörére Másik példa: a tömbök indexelése mindenhol kezdődjön 0-val!.NET programnyelvek: Basic, C++, C#, JScript, J# Mindegy, melyikben programozunk, mert mindegyik kód a CLR virtuális gépi kódjára lesz lefordítva. Több nyelvet is használhatunk egy programon (projekten) belül! 6
7 Microsoft Visual C# Express: Ingyenesen letölthető, ingyen használható Csak SQL Server Express és Access támogatás Nem lehet vele mobileszközökre fejleszteni Kereskedelmi forgalomba kerülő alkalmazásokat nem lehet vele fejleszteni A C# nyelv jellemzői: C/C++ szintaktika Szigorúbb szabályok (nehezebb rossz kódot írni) Teljesen objektumorientált (fogalom magyarázata később) Nincsen közvetlen mutatókezelés (biztonságosabb) Letölthető összetevők: Visual C# Express fejlesztőkörnyezet SQL Server Express adatbázisszerver MSDN Library Express súgórendszer Visual Web Developer (ASP.NET fejlesztéshez) A VISUAL C# 2008 KÖRNYEZET LETÖLTÉSE, TELEPÍTÉSE Bár a jegyzet a Visual C# 2008-as verziójához készült, a benne leírtak gond nélkül alkalmazhatók a 2010-es vagy későbbi verziókra is. A Microsoft Visual C# weboldala: A weboldalon az adott programozási nyelvvel kapcsolatos információkat a másodlagos menüsorban, míg az általános információkat a Support menüpontban találod meg Milyen konfigurációjú számítógép szükséges a Visual C# 2008 Express futtatásához? Keresd meg a hardverkövetelményeket a fent megadott oldalon! Töltsd ki a táblázatot! Windows XP Windows Vista / 7 Windows Server 2008 / 2008 R2 Processzor (CPU) Memória (RAM) 7
8 1.2. A fenti táblázat alapján az iskolai / otthoni gép megfelel-e a C# 2008 hardver- és szoftverkövetelményeinek? Ha nem, írd le, melyik miatt nem felel meg! Igen Nem ábra A C# telepítése 1.3. Telepítsd fel a Visual C# 2008-at az iskolai számítógépre, amennyiben alkalmas a futtatására, és még nincsen rajta! Gyűjtsd össze, hogy a fent látható komponensen kívül még mit tölt le és telepít a számítógépre! 8
9 Amennyiben az iskolai gép már tartalmaz feltelepített C# 2008-at, akkor telepítsd fel az otthoni gépedre! A VISUAL C# 2008 KÖRNYEZET MEGISMERÉSE, TESTRE SZABÁSA 1.3. ábra Windows Forms alkalmazás létrehozása 1.4. Kattintsunk a Create pontra, és hozzunk létre egy új Windows Forms alkalmazást! Nevezzük ElsoProgramom-nak! Mentsük el a merevlemez valamelyik mappájába! A projekt (Project) tartalmazza az összes forrásállományt és a hozzájuk tartozó formokat, ezek alkotják a lefordításra váró programunkat. A Solution eggyel magasabb szintet jelent, több projektet is egységbe foghat, azonban a mi példáinkban mindig csak egyet tartalmaz majd A következő oldalon található ábrán nevezd meg a kezelőfelület legfontosabb elemeit! Nem biztos, hogy telepítés után a kezelőfelület összes szükséges eleme látható. Amennyiben valami hiányzik, a View menüben be tudod kapcsolni! 9
10 1.4. ábra A kezelőfelület elemei 1.6. Melyik elemnek mi a feladata? Írd be az üres oszlopba a kérdéses elemhez tartozó feladatleírás betűjelét! 1. Toolbox a) a Solutionhöz tartozó projektek, és azok állományait tartalmazó ablak 2. Error list b) az objektumok tulajdonságait és eseményeit tartalmazó ablak 3. Form Design nézet c) a Windows alkalmazás fejlesztéséhez felhasználható komponensek gyűjteménye 4. Properties d) a form grafikai megtervezését megkönnyítő ablak 5. Solution Explorer e) hibák, figyelmeztetések, üzenetek ablaka 10
11 1.7. Írd le, hogyan tudunk váltani a Form kinézete (Design) és kódja között! A videókban láthattad, hogy miképp lehet fogd és vidd (Drag and Drop) módszerrel átrendezni a kezelőfelület elemeit A tanult módszerrel rendezd át többféleképpen a kezelői felületet! Az egyes állapotokat örökítsd meg képernyőfelvétellel (Alt+PrintScreen), és ezeket mentsd külön képfájlokba, majd add be a tanárodnak! 1.5. ábra A felület testre szabása 1.9. Mire való a kis rajzszög ikon az eszközpaletták jobb felső sarkában? 11
12 A SÚGÓ HASZNÁLATA 1.6. ábra A Súgó Gyűjtsd ki a Súgóból a C# Express 2008 legfontosabb újításait címszavakban! (What s new in Visual C# Express) Keress a Súgóban példát tömbök deklarációjára, és írj le ide legalább két példát! (arrays, examples) 12
13 1.12. A Súgó Search funkcióját használva keress rá a foreach kulcsszóra! Írd le a példakódot, amit találsz! Az interneten óriási mennyiségű információt, mintakódot találhatsz a Microsoft MSDN Library weboldalán: 13
14 WINDOWS FORMS ALKALMAZÁS KÉSZÍTÉSE Ennek a témakörnek az a célja, hogy el tudd készíteni életed (valószínűleg) első Windows Forms alkalmazását. Tudj megtervezni egy felhasználói felületet, a komponenseket igényesen elhelyezni, igazítani, alapvető tulajdonságaikat beállítani (részben tervezési, részben futási időben). Legyél képes eseménykezelő metódusokba kódot írni, a megírt kódot áthelyezni, illetve eltávolítani. ELMÉLETI ÖSSZEFOGLALÓ Objektumorientált programozás alapfogalmak: A mindennapi életben gyakran osztályokba soroljuk a dolgokat. Pl.: emberek (azon belül fiúk, lányok), állatok (azon belül emlősök, hüllők), járművek (azon belül szárazföldi, vízi és légi járművek). Az osztály tagjait elsősorban a közös tulajdonságok és a közös viselkedés kapcsolja össze. Közös tulajdonság: a szárazföldi járműveknek többnyire van kereke. Közös viselkedés: a lányok többnyire sminkelik magukat. Osztály: azonos tulajdonságokkal (jellemzőkkel) és viselkedéssel rendelkező egyedeket zárja egységbe. A tulajdonságok értékét adatok tárolják. A viselkedéseket pedig metódusok írják le. Objektum = az osztály egy példánya, egyede (osztály típusú változó), példa: nyomógomb egy Windows Forms alkalmazásban. A Windowsban minden objektummal események történnek, például: gombra kattintás, Form betöltődése. Az események bekövetkezésekor lefutó alprogramokat eseménykezelő metódusoknak hívjuk. A metódusnak más programozási nyelvekben az eljárás vagy függvény felel meg, példa: gombra kattintáskor lefutó button1_click metódus. 14
15 Komponensek alaptulajdonságai: A tulajdonságok értéke beállítható tervezési időben (a fejlesztőkörnyezetben) és futási időben (programkódból). Name = az objektum neve Nem kezdődhet számmal, nem lehet benne szóköz és speciális karakter Olyan szerepet tölt be, mint a változónév Text = az objektum (gomb, címke, Form, csoport, jelölőnégyzet, rádiógomb) felirata AutoSize = automatikus méretezés BackColor = háttérszín Width = szélesség Height = magasság Left = távolság az ablak (vagy képernyő) bal szélétől Top = távolság az ablak (vagy képernyő) tetejétől Enabled = engedélyezett (például egy gombra lehet kattintani) Visible = látható vagy nem látható Tulajdonság értékének beállítása kódból: Értékadás általános formája: objektumnév.tulajdonság = érték; Példák értékadásra: label1.text = "Hello, világ!"; label1.autosize = false; label1.backcolor = Color.Red; label1.width = 50; label1.height = 30; label1.left = 100; label1.top = 200; label1.enabled = true; label1.visible = false; Az értékadás formája a tulajdonság típusától függ! 15
16 A leggyakoribb esemény: kattintás (Click). Példa: gombra kattintáskor változzon a címke felirata. private void button1_click(object sender, EventArgs e) label1.text = "Hello, világ!"; Metódus feje Metódus törzse A metódus fejét a fejlesztőkörnyezet elkészíti, nekünk csak a törzsét kell megírni! WINDOWS FORMS ALKALMAZÁS KÉSZÍTÉSE 2.1.Készíts új Windows Forms alkalmazást a Visual C# 2008-ban! Tervezd meg a következő ábrán látható program felhasználói felületét, úgy, hogy minél jobban hasonlítson a képen látottakra! Emlékeztetőül feltüntettük, hogy melyik vezérlőelemet milyen név alatt találod meg ábra Példa Windows Forms alkalmazás Fontos megjegyezni, hogy ebben a fejezetben ennek a programnak csak a felhasználói felületét készítjük el, a mögötte lévő kódokat a későbbi fejezetek feladataiban írjuk meg. 16
17 A teljes feladat: Az adat.txt állományban országok neve és területük szerepel. Az adatok betöltését és listába írását az 5.2. feladatban írjuk majd meg. A 6. fejezetben tanuljuk a programozási tételeket, ezek segítségével a 6.2. feladatban kiszámoljuk a területek átlagát, megszámoljuk a kiválasztott feltételnek megfelelő elemek számát, illetve fájlba is kiírjuk azokat. Meghatározzuk továbbá a legkisebb, illetve legnagyobb területű országot, illetve megkeresünk egy, a felhasználó által megadott nevű államot, amennyiben szerepel az adatok között Amikor elkészültél a komponensek felrakásával és elrendezésével, állítsd be az alább felsorolt tulajdonságokat a fejlesztői környezetben, a Properties ablakban! A formon: o Felirat: Példa Windows Forms alkalmazás o Méret: amekkora területen kényelmesen elférnek a komponensek o FormBorderStyle: FixedSingle (ne lehessen átméretezni) o MaximizeBox: false (ne lehessen teljes képernyőre nagyítani) Mind a négy címkén a megfelelő feliratot (pl.: Forrásfájl neve: ) Mind a hat gombon a megfelelő feliratot (pl.: Területek átlaga ) A megfelelő szerkesztőmezők Text tulajdonságába írd be a fájlok nevét, a harmadik mezőt hagyd üresen! A listán nem kell semmit beállítani Állítsd be a csoportosító (GroupBox) feliratát, és ügyelj arra, hogy a két RadioButton komponens a csoporton belül legyen! Ügyelj rá, hogy valamelyik rádiógomb Checked tulajdonságát igazra állítsd (így be lesz jelölve)! A kombinált lista Items tulajdonságánál kattints a -ra, majd a megjelenő ablakba írd be két külön sorba a Minimum és a Maximum szavakat. A DropDownStyle tulajdonságot állítsd DropDownList-re, így a felhasználó csak kiválasztani tud a listából, a benne lévő szöveget nem tudja szerkeszteni. A jelölőnégyzet bejelölt (Checked) tulajdonságát tetszés szerint állíthatod. Szeretnénk elérni, hogy a kombinált listából induláskor automatikusan válasszuk ki az első elemet (különben a program úgy indul, hogy üres). Ezt csak kódból tehetjük meg. Írjuk meg a form Load eseménykezelő metódusát, ami akkor fut le, amikor az alkalmazás ablaka megjelenik a képernyőn. Írjuk bele az alábbi sort: combobox1.selectedindex = 0; A SelectedIndex tulajdonság azt adja meg, hogy a kombinált lista hányadik sorszámú eleme a kijelölt. A C#-ban mindent (tömböt, listát) nullától kezdve számoznak, ezért az első elemre mindenhol így kell majd hivatkozni. 17
18 2.3.Mentsd el az elkészült programodat, és helyezd biztonságba, mert a későbbi fejezetekben ez alapján fogunk dolgozni! Add be a projektet összecsomagolva a tanárod által megadott helyre is! 2.4.Készítsünk egy teljesen új projektet, amellyel gyakorolhatjuk a különféle tulajdonságok beállítását! ábra Tulajdonságok gyakorlása, kezdeti változat Mentsük el a projektet Tulajdonsagok_Gyakorlas néven! Változtassuk meg a Properties ablakban a form nevét Form_Gyakorlas-ra! Nézzük meg a kódban, mi változott! Helyezzünk el a formra egy címkét és két gombot! Az első gomb változtassa meg a saját feliratát a Form beállítása, illetve a másik gomb feliratát a Címke beállítása szövegre! Ha lefuttatjuk a programot, akkor a gombokon levő szövegnek csak egy része látszik. Melyik tulajdonságot kell megváltoztatni, hogy ezt a hibát kijavíthassuk? Amennyiben sikerült rájönnünk, javítsuk a hibát! Az első gombra kattintva változtassuk meg a form feliratát a Tulajdonságok gyakorlása szövegre! Amennyiben az eddigiek alapján az alábbi kódot írjuk, nem működik: Form_Gyakorlas.Text = "Tulajdonságok gyakorlása"; Próbáljunk rájönni, hogy miként lehetne mégis megoldani! Az egyik lehetséges megoldás, hogy a form saját magára a neve helyett a this szócskával hivatkozhat: this.text = "Tulajdonságok gyakorlása"; 18
19 2. 5. ábra Tulajdonságok gyakorlása, színbeállítás után Az előzőek alapján az első gombra kattintva állítsuk be a form háttérszínét CornflowerBlue színűre! Most a második gomb következik. Rákattintáskor elsőként tiltsuk le az első gombot! Következő lépésben tiltsuk le a címke automatikus méretezését, és állítsuk be a címke méretét 100*50 pixelre! Mivel így nem látunk semmit, a címke háttérszínét állítsuk át valamilyen tetszőleges (a form háttérszínétől különböző színre!) A címke pozícióját változtassuk meg úgy, hogy az ablak bal szélétől és tetejétől egyaránt 10 pixel távolságra legyen! A címkére való kattintáskor a címke felirata helyezkedjen el vízszintesen és függőlegesen egyaránt középen! A címkén levő szöveg igazítását a TextAlign tulajdonság határozza meg. Nézz utána, hogy ennek értékét miként lehet kódból beállítani! Tipp: a fejlesztői környezet automatikus kódkiegészítés funkciója segíthet ebben. A címkére kattintáskor növeljük meg a betű méretét 2 ponttal! Sajnos ez sem megy olyan egyszerűen. Az alábbi értékadás ugyanis nem működik: label1.font.size = label1.font.size + 2; Ehelyett az alábbi, kicsit komplikált utasítást kell használni: label1.font = new Font(label1.Font.FontFamily, label1.font.size + 2); Ennek jelentése: a címke betűtípusának egy olyan új betűtípus objektumot adunk értékül, amelynek betűstílusa olyan, mint ami korábban volt, csak a betűméret lett a korábbinál 2 ponttal nagyobb. Utolsóként tegyünk egy újabb gombot a formra, és a címke Click eseménykezelő metódusának kódját egy az egyben mozgassuk át az új gomb Click metódusának törzsébe! Egészítsük ki, hogy a címke méretét növelje meg mindkét irányban 10 pixellel! 19
20 2. 6. ábra Tulajdonságok gyakorlása, három gombbal A címke üres Click eseménykezelő metódusát nem törölhetjük ki a kódból, helyette a Properties ablak Events gombjára való kattintás után töröljük ki a label1_click hivatkozást. Ezután futtatva a programot, majd megnézve a kódot, láthatjuk, hogy az üres eseménykezelő metódust törölte a fejlesztői környezet. 2.5.Készíts olyan programot, amely tartalmaz négy különböző hátterű címkét, és kattintáskor mindegyik a saját háttérszínére színezi át a form háttérszínét! 2.6.Az ügyesebbek készítsék el úgy is a programot, hogy helyezzenek el négy tetszőleges háttérszínű címkét, amelyek rájuk kattintáskor cseréljenek háttérszínt a formmal! 20
21 ADATOK BEOLVASÁSA ÉS KIÍRÁSA, ALAPVETŐ ADATTÍPUSOK, TÍPUSKONVERZIÓK, MATEMATIKAI ÉS LOGIKAI MŰVELETEK Ennek a témakörnek az a célja, hogy kicsi, egyszerűbb programok elkészítése közben begyakorold a változók deklarálásának módját, az értékadásokat, az egyes adattípusokat és a velük végezhető műveleteket. Mivel grafikus programokat írunk, észrevétlenül megtanulod, hogyan kommunikálj a felhasználóval a grafikus komponensek segítségével. ELMÉLETI ÖSSZEFOGLALÓ Változó: egy adott memóriaterület, amelyre névvel hivatkozunk. A változó neve nem kezdődhet számmal, nem tartalmazhat speciális karaktert (pl. szóköz, kérdőjel, felkiáltójel, stb.). Mindig tartozik hozzá típus. A típus az alábbi három jellemzőt foglalja magába: típusértékhalmaz: milyen halmazból veheti fel az értékeit, műveletek: milyen műveleteket lehet végezni ezeken az értékeken, szerkezet: hogyan épül fel más típusokból (csak összetett típus esetén). Egyszerű (alap) típusok, műveletek: Egész szám: int Műveletek: + - * / (egész osztás) % (maradék) Valós szám: double Műveletek: + - * / (valós osztás) Karakter: char Műveletek: + (összefűzés) Szöveg (karakterlánc): string Műveletek: + (összefűzés) Logikai: bool Műveletek: && (és) (vagy)! (nem) Deklaráció: mielőtt a változót használjuk, deklarálnunk kell (azaz meg kell mondanunk a típusát), de ezt csak egyszer tehetjük meg! Formája: típus változónév; Példa: int x; Ettől még nem tudjuk használni, nincs értéke! 21
22 Értékadás: megfelelő típusú érték hozzárendelése a változóhoz. Formája: változónév = érték; Példa: x = 5; Deklaráció és kezdőérték adása együtt: Példa: int x = 5; Alapvetően az értékadás bal és jobb oldalán egyforma típusú értéknek kell állnia (nem mindig, ld. később). Példa értékadásokra: int x = 5; double pi = 3.14; char c = 'A'; string s = "Helló, világ!"; bool van = false; Figyeljük meg a példákon, hogy a tizedestörtben tizedespont van, illetve egyetlen karaktert aposztrófok közé kell írni! Mi történik a következő értékadásnál? double y = 5 + 3; A jelenség neve implicit (rejtett) típuskonverzió. Az értékadás előtt a 8 értéket valós számmá alakítja, így az y értéke 8.0 lesz. Mi történik a következő értékadásnál? double y = 5 / 3; Mivel az 5 és a 3 is egész számok, ezért köztük egész osztást végez, így az y értéke 1.0 lesz! Megoldás: double y = 5.0 / 3; (vagy 5 / 3.0) Tegyük fel, hogy a következő változóink vannak: int ossz = 17; (jegyeink összege) int db = 5; (jegyeink darabszáma) 22
23 Szeretnénk átlagot számolni. Az előbbiek alapján az alábbi utasítás nem jó: double atl = ossz / db; (eredmény: 3.0) Megoldás: típuskényszerítés. double atl = ossz / (double)db; Az osztás elvégzése előtt a db-ből valós számot csinált (5.0 értékkel), így az átlag értéke 3.4 lesz. Adatbevitel grafikus felületen: Adatok bevitelére a TextBox komponenst használhatjuk. Fontosabb tulajdonságai: Text = a beleírt szöveg (string típusú!) ReadOnly = csak olvasható (kiírásra is használható) MaxLength = a beleírható maximális karakterszám Mikor vizsgáljuk meg a tartalmát? Tegyünk mellé egy gombot, és annak Click eseményének bekövetkeztekor! Jó-e a következő utasítás? int x = textbox1.text; Nem, mert a bal oldalon egy int, a jobb oldalon pedig string típusú érték található. Megoldás: explicit (közvetlen) típuskonverzió. Formája: új_típus.parse(kifejezés) Például: int x = int.parse(textbox1.text) Jellemzően int, double, char, bool típusokra lehet string típusú értéket konvertálni ezzel a módszerrel. Viszont ezeket a típusokat string típusú értékre másképp lehet konvertálni. Adatok kiírása grafikus felületen: Változók string típusra konvertálása: változónév.tostring() Például eredmény megjelenítése címkén: label1.text = "Az x értéke: " + x.tostring(); A + jellel tudunk szövegeket összefűzni! Ugyanennek kiírása üzenetablakba: MessageBox.Show(üzenet); Példa: MessageBox.Show("Az x értéke: " + x.tostring()); 23
24 KÖR KERÜLETÉNEK ÉS TERÜLETÉNEK KISZÁMÍTÁSA 3.1.Készíts új Windows Forms alkalmazást a Visual C# 2008-ban! Helyezd el a komponenseket az alábbi képhez hasonló módon, és állítsd be a tulajdonságaikat! A program olvassa be egy kör sugarát és számítsa ki a kerületét, területét! 3.7. ábra A kör kerületét és területét számoló alkalmazás Kattints duplán a gombra, ekkor megnyílik a kódszerkesztő, benne a gomb üres Click eseménykezelő metódusával. Deklaráld a kör sugarát tároló változót! Ez lehet egész vagy valós szám. A szerkesztőmező tartalmát konvertáld át a megfelelő típusra, és add értékül a változónak! int r = int.parse(textbox1.text); Figyeljünk arra, hogy explicit típuskonverziónál az értékadás mindkét oldalán azonos típusú változók legyenek! Például valós típus esetén: double r = double.parse(textbox1.text); Deklarálj még két változót, amelybe számold ki a kerületet és a területet. Mivel a kör kerületének és területének képlete tartalmazza a π-t, ezért mindenképpen valós típusú változókra lesz szükség! A C#-ban rengeteg matematikai függvény és konstans megtalálható beépítve. Így például a π értékét a Math.PI segítségével kaphatod meg. Nézd meg, hogy milyen függvények érhetők el még a Math osztályban! A kerület és terület kiszámítása: double k = 2 * r * Math.PI; double t = r * r * Math.PI; Már csak a kiíratás van hátra. Írd ki a két címkére a megfelelő szövegeket, és utánuk a két változó értékét. Figyelj arra, hogy ehhez a k és t értékét szöveggé kell konvertálnod! 24
25 label3.text = "A kör kerülete: " + k.tostring(); label4.text = "A kör területe: " + t.tostring(); Töröld ki a két címkét, és jelenítsd meg az eredményeket üzenetablakban! MessageBox.Show("A kör kerülete: " + k.tostring()); MessageBox.Show("A kör területe: " + t.tostring()); A két kiíratási mód között alapvető különbség, hogy az első esetben a szöveget egy tulajdonságnak adjuk értékül, a másodiknál pedig egy metódusnak adjuk át paraméterül! Figyeljünk az eltérő szintaxisra! Nézz utána, hogy hogyan lehet megoldani, hogy csak két tizedesjegyig írja ki a valós számokat! LOGIKAI MŰVELETEK 3.2.Készíts új Windows Forms alkalmazást a Visual C# 2008-ban! Helyezd el a komponenseket az alábbi képhez hasonló módon, és állítsd be a tulajdonságaikat! A program segítségével gyakoroljuk a logikai műveleteket, és közben megtanuljuk a jelölőnégyzet használatát ábra Logikai műveletek gyakorlása A jelölőnégyzet rendelkezik Checked tulajdonsággal, ami igaz értéket vesz fel, amikor bejelölt állapotban van. A jelölőnégyzetek egymástól függetlenül lehetnek bejelölve. A következő fejezetben rádiógombokat használunk majd az egymást kizáró lehetőségek vizsgálatára. Összetett feltételek megfogalmazásához az alábbi operátorok állnak rendelkezésünkre: && (logikai ÉS), (logikai VAGY),! (logikai NEM). Az Állapot váltása gomb Click eseménykezelő metódusába írjunk olyan kódot, amely a jelölőnégyzetek állapotát változtatja ellenkezőjére a logikai NEM művelet segítségével: checkbox1.checked =!checkbox1.checked; checkbox2.checked =!checkbox2.checked; A két értékadás eredményeképpen a jelölőnégyzetek Checked tulajdonsága ellenkezőjére változik (ha igaz volt, hamissá, és fordítva). 25
26 A Logikai ÉS művelet gombra kattintáskor deklaráljuk egy logikai változót, melynek értéke legyen a két jelölőnégyzet Checked tulajdonsága a logikai ÉS művelettel összekapcsolva! Az eredményt jelenítsük meg üzenetablakban! bool l = checkbox1.checked && checkbox2.checked; MessageBox.Show("A logikai ÉS eredménye: "+l.tostring()); A másik nyomógombra csináljuk meg ugyanezt a logikai VAGY művelettel! bool l = checkbox1.checked checkbox2.checked; MessageBox.Show("A logikai VAGY eredménye: " + l.tostring()); A LISTBOX HASZNÁLATA 3.3.Készíts új Windows Forms alkalmazást a Visual C# 2008-ban! Helyezd el a komponenseket az alábbi képhez hasonló módon, és állítsd be a tulajdonságaikat! 3.9. ábra Egyszerű mintapélda a ListBox használatára A ListBox egy nagyon fontos és gyakran használt komponensünk. Segítségével egy listát tudunk kiírni a képernyőre, úgy, hogy az információkat soronként tudjuk beleírni. Négy egyszerű művelettel kezelhetjük a listát: 1. Egy sor hozzáadása a listához: listbox1.items.add(s); Megjegyzés: az Add paraméterének string típusú adatnak kell lennie! 2. Kijelölt elem sorszáma (-1 az eredmény, ha nincs ilyen): int x = listbox1.selectedindex; Megjegyzés: csak úgy, mint a ComboBox esetén! 3. Adott sorszámú elem törlése: listbox1.items.removeat(sorszám); Megjegyzés: itt is nullától indul a számozás! 4. Minden elem törlése a listából: listbox1.items.clear(); 26
27 A fentiek alapján próbáljuk meg önállóan megírni ezt a kis programot. A Hozzáad gombra kattintva a lista feletti szerkesztőmező tartalmát adja a listához, majd törölje a mező tartalmát! A Kijelölt törlése értelemszerűen törölje ki a lista kijelölt elemét, az alatta levő gomb pedig távolítsa el a lista valamennyi elemét! FELADATOK 3.4.Egészítsd ki a 3.1. feladatban megírt programot úgy, hogy a kerület és a terület mellett a sugarat egy gömb sugaraként kezelve számolja ki annak felszínét és térfogatát! Az eredményt jelenítsd meg címkén vagy üzenetablakban! 3.5.Írj a 3.1. feladat alapján programot, amely bekéri egy téglalap két oldalhosszúságát, és ezek alapján kiszámolja a kerületét és területét! Az eredményeket vezesse a program egy listában, az alábbi ábrán láthatóhoz hasonlóan! ábra Téglalap kerületét, területét számoló program listával 3.6.Írj programot, amely beolvassa a másodfokú egyenlet három paraméterét (a,b,c), majd a megoldóképlet segítségével megadja a két megoldást! (Egyelőre az egyszerűség kedvéért tegyük fel, hogy van megoldása az egyenletnek!) A négyzetgyököt számító függvény a Math.Sqrt. Mivel az elágazásokat még nem tanultuk, ezért nem tudjuk kezelni azt a helyzetet, ha nincsen valós megoldás. Később, a 8.3-as feladatban kibővítjük ezt a programot, úgy, hogy minden lehetséges esetet megfelelően kezeljen. 3.7.Írj programot, amely bekéri egy derékszögű háromszög két befogójának hosszát, és ebből kiszámolja az átfogó hosszát (Pitagorasz-tétellel)! Az eredményt ismét vagy címkén, vagy üzenetablakban jelenítsük meg! 27
28 VEZÉRLÉSI SZERKEZETEK: ELÁGAZÁSOK, CIKLUSOK Ennek a témakörnek az a célja, hogy egyszerű programok elkészítése közben begyakorold a legfontosabb vezérlési szerkezetek, az elágazások és a ciklusok használatát. A fejezet végére megtanulsz egy- és kétirányú, valamint többirányú elágazást írni, és tisztában leszel a ciklusok fajtáival, helyes szintaktikájukkal és alkalmazhatóságukkal is. ELMÉLETI ÖSSZEFOGLALÓ Gyakran előfordul, hogy a programban döntési helyzet alakul ki, azaz bizonyos feltétel(ek) teljesülésétől függően mást kell csinálnunk, mint amikor azok nem teljesülnek. Ezt hívjuk elágazásnak. Fajtái: egy-, illetve kétirányú elágazás többirányú elágazás. Egyirányú elágazás: if (feltétel) utasítások; Kétirányú elágazás: if (feltétel) utasítások; else utasítások; Egymásba ágyazott elágazások: if (feltétel 1 ) utasítások; else if (feltétel 2 ) utasítások; else if (feltétel n ) utasítások; else utasítások; Például: if (x>0) label1.text="pozitív"; else if (x<0) label1.text="negatív"; else label1.text="nulla"; 28
29 Elágazás egy változó értékétől függően: switch (kifejezés) case érték 1 : utasítások; break; case érték 2 : utasítások; break; case érték 3 : utasítások; break; default: utasítások; break; Szabályok: Intervallumot nem lehet megadni, és több értéket sem lehet felsorolni. Nem lehet két sorban a case után ugyanaz az érték. Ha a case után nem írunk utasítást, akkor a következő soron folytatódik a végrehajtás. A default utáni utasításokat akkor hajtja végre, ha egyik felsorolt érték sem egyezik meg a kifejezés értékével. A break utasítás minden ág végén kötelező! Ciklusokat akkor használunk, amikor egy adott tevékenységet (ciklusmag) többször végre kell hajtanunk. Azokat a változókat, amelyektől a ciklus futása függ, ciklusváltozónak hívjuk. Fajtái: számláló: előre megadott számú alkalommal fut le a ciklusmag. elöltesztelő: egy feltétel teljesülésétől függően hajtja végre a ciklusmagot (előfordulhat, hogy egyszer sem, ha a feltétel már kezdetben sem teljesül) hátultesztelő: egyszer mindenképpen végrehajtja a ciklusmagot, majd addig ezt ismétli, amíg a feltétel teljesül. Számláló ciklus: for ( kezdıérték-adás; feltétel; ciklusváltozó növelése) utasítások; Példa: for (i=0; i<10; i++) listbox1.items.add(i.tostring()); A példában az i a ciklusváltozó, amely sorban felveszi a 0, 1, 2, 3,, 9 értékeket, ezeket beírja a listába, majd a ciklus futása befejeződik. Megjegyzés: az i++ jelentése: i = i
30 Elöltesztelő ciklus: while (feltétel) utasítások; Példa: while (!f.endofstream) s = f.readline(); listbox1.items.add(s); A példában addig olvasunk be egy szövegfájlból, amíg nem érünk a végére. Itt a ciklusváltozó szerepét az f fájlváltozó játssza. A for és a while ciklusok lényegében egyformák a C# nyelvben. Példa: for (i=0; i<10; i++) listbox1.items.add(i); int i=0; while (i<10) listbox1.items.add(i); i++; 30
31 Ez alapján bármelyik átírható a másikra! Figyeljünk oda, hogy a while ciklusból ne maradjon ki a ciklusváltozó változtatása, mert az végtelen ciklushoz vezet! Hátultesztelő ciklus: do utasítások; while (feltétel); Példa: i=0; ossz=0; do i++; ossz += i; // jelentése: ossz = ossz + i while (i<10); A példában 1-től 10-ig összeadjuk a számokat. 31
32 ELÁGAZÁSOK 4.1.A 3.1 feladatban elkészített, a kör kerületét és területét számoló programot fogjuk módosítani. Keresd elő az elkészített programot, másold le a mappáját az új könyvtárba, nyisd meg, és hajtsd végre rajta a szükséges módosításokat, annak érdekében, hogy hasonlítson az alábbi ábrán láthatóra! ábra Elágazás: kör vagy négyzet? A rádiógombokat csoportba kell foglalni (a GroupBox komponens segítségével), mert így az egy csoportban levők közül pontosan egy lehet bejelölt állapotban. Ennek segítségével tudunk olyan lehetőségek közül választani, amelyek egymást kizárják. Helyezzünk el a formon egy GroupBox komponenst, és tegyünk bele két rádiógombot! Három dolgot szeretnénk megvalósítani a programban: Ha rákattintunk a kör, illetve a négyzet feliratú rádiógombra, a beviteli mező feletti felirat változzon a megfelelőre. A gombra kattintás után a kiválasztott alakzat kerületét, és területét számoljuk ki. Amennyiben a felhasználó nem írt be semmit a szövegmezőbe, a gombra kattintás ne csináljon semmit. A jelenlegi verzióban a program végrehajtása kivételt dobva leáll. A rádiógomb kijelölésének megváltozásakor a CheckedChanged esemény következik be. Írjuk meg ezeket a metódusokat, amelyek változtassák meg a címke feliratát: label1.text = "A kör sugara:"; A másik hasonlóan írandó, a négyzet oldalhosszára vonatkozóan. Annak ellenőrzése, hogy melyik alakzatot választották ki, nagyon egyszerű. A rádiógomb Checked tulajdonsága logikai értékű, és mivel egyszerre csak az egyiket tudjuk bejelölni, ezért elegendő egy kétirányú elágazást használni. Helyezzük át a kör kerületét és területét számoló programrészt az elágazás egyik ágába, majd a másik ágba átmásolva írjuk át úgy, hogy négyzetre számoljon! 32
33 int r = int.parse(textbox1.text); double k; double t; if (radiobutton1.checked) k = 2 * r * Math.PI; t = r * r * Math.PI; label3.text = "A kör kerülete: " + k.tostring(); label4.text = "A kör területe: " + t.tostring(); else k = 4 * r; t = r * r; label3.text = "A négyzet kerülete: " + k.tostring(); label4.text = "A négyzet területe: " + t.tostring(); Figyeljük meg, hogy a példában szétválasztottuk a változók deklarációját és az értékadásokat. Egy blokkon belül ne deklaráljuk újra a változókat, mert fordítási hibát kapunk! Már csak az utolsó problémát kell megoldanunk. Tegyük az egész fenti kódot egy elágazásba, amely megvizsgálja, hogy a szövegmező tartalma nem üres! if (textbox1.text!= "") C#-ban az alábbi összehasonlító operátorokat használhatjuk: < (kisebb), <= (kisebb vagy egyenlő), > (nagyobb), >= (nagyobb vagy egyenlő), == (egyenlőségvizsgálat),!= (nemegyenlőség-vizsgálat). Bővítsd ki ezt az utolsó egyirányú elágazást kétirányúvá, hogy ha mégsem írt be semmit a felhasználó, udvarias üzenetben figyelmeztesse erre a program! 4.2.Írjunk programot, amely egy megadott számról megállapítja, hogy a felhasználó által megadott intervallumba esik-e! A választ címkére írjuk ki! Tervezzük meg a felületét az alább látható módon! 33
34 4.12. ábra Intervallumos program A programban két elágazásra lesz szükségünk. Az egyik ellenőrzi, hogy valóban intervallumról van-e szó (alsó határ kisebb vagy egyenlő, mint a felső határ), a másik pedig azt vizsgálja, hogy a szám a megadott intervallumba esik-e. int ah = int.parse(textbox1.text); int fh = int.parse(textbox2.text); int szam = int.parse(textbox3.text); if (ah <= fh) if (szam >= ah && szam <= fh) label4.text = "Benne van az intervallumban."; else label4.text = "Nincs benne az intervallumban."; else label4.text = "Ez nem intervallum!"; A példakódban is látszik a logikai műveletek használata. A (szam >= ah && szam <= fh) jelentése: egyszerre kell teljesülnie annak a feltételnek, hogy a szám értéke nagyobb vagy egyenlő, mint az alsó határ, és kisebb vagy egyenlő, mint a felső határ. 4.3.Írjunk programot, amely egy számmal beírt jegyet szövegesen is megjelenít egy címkén! A programot tervezzük meg az alább látható módon! ábra Többirányú elágazás gyakorlása Programunkban többirányú elágazásra van szükség. Egy egész típusú változóba vegyük ki a beírt számot, és a megfelelő jegyhez írjuk ki a címkére a szöveges megnevezést. A default ág segítségével hibaüzenetet is tudunk adni, ha nem egy és öt közötti számot írt be a felhasználó. 34
35 int jegy = int.parse(textbox1.text); switch (jegy) case 1: label2.text = "Elégtelen"; break; case 2: label2.text = "Elégséges"; break; case 3: label2.text = "Közepes"; break; case 4: label2.text = "Jó"; break; case 5: label2.text = "Jeles"; break; default: label2.text = "Nem osztályzat!"; break; CIKLUSOK 4.4.Következő programunkban a formot fogjuk mozgatni, számláló és elöltesztelő ciklus segítségével, felváltva. A formon csak egyetlen gomb legyen, melynek felirata mondjuk Indít! legyen. Ennek lenyomására a formot tegyük a bal felső sarokba, majd mozgassuk körbe a képernyő szélein! Egy kör megtétele után álljon meg a bal felső sarokban! Emlékeztető: a form tulajdonságaira a this kulcsszóval tudunk hivatkozni. Első lépésként helyezzük át a formot a bal felső sarokba. A következő utasításokat a gomb Click metódusába kell írnunk. this.left = 0; this.top = 0; Szükségünk lesz arra, hogy a form meddig mehet a képernyőn, hogy még éppen benne maradjon. Ehhez a képernyő vízszintes felbontásából ki kell vonnunk a form szélességét, a függőlegesből pedig a magasságát. Ezt az alábbi két utasítással tehetjük meg: int kepszel = Screen.PrimaryScreen.Bounds.Width this.width; int kepmag = Screen.PrimaryScreen.Bounds.Height this.height; Először egy számláló ciklussal mozgassuk a formot vízszintesen a kép széléig! Ciklusváltozónak használhatjuk a this.left értéket, hiszen ez határozza meg a form bal felső sarkának távolságát a képernyő bal szélétől. for (this.left = 0; this.left < kepszel; this.left++) Figyeljük meg a fenti példában, hogy a ciklus magja üres, viszont a C# for ciklusának sajátossága miatt (a Left értéket minden lépésben növeli) mégis működik így is! Most használjunk egy while ciklust a lefelé mozgatáshoz! while (this.top < kepmag) this.top++; 35
36 Természetesen ez a ciklus is bármikor átírható számláló ciklusra. A visszafelé mozgatást hasonló ciklusokkal tudjuk megcsinálni, csak növelés helyett csökkentenünk kell a tulajdonságok értékét, és addig megyünk, amíg a nullát el nem érik. for (this.left = kepszel; this.left > 0; this.left--) while (this.top > 0) this.top--; 4.5.Írjunk olyan programot, amely ismételt adatbevitellel (ciklusban) kér be a felhasználótól számokat, majd ezeket szétválogatja párosakra és páratlanokra! Ehhez tervezzük meg az alábbi ábrán látható alkalmazást! ábra Adatbevitel ciklusban, szétválogatás Eddig nem volt szükségünk arra, hogy ciklusban egymás után több adatot olvassunk be. A mindennapi életben azonban ez gyakran előfordul. A szövegmező erre nem alkalmas, ezért a Visual Basic-től kölcsönvesszük az InputBox metódust. Egy Visual Basic modul használatához két beállítás szükséges. Kattintsunk jobb gombbal a Solution Explorer ablakban a References pontra, és válasszuk az Add reference menüpontot. A megjelenő ablakban válasszuk a Microsoft.VisualBasic sort, és kattintsunk az OK gombra. (Lásd: 5. ábra.) A Form1.cs kódban, legfelül találunk jó pár using kezdetű sort. Adjuk hozzá az alábbi sort: using Microsoft.VisualBasic; 36
37 4.15. ábra Visual Basic referencia hozzáadása Most már használhatjuk az InputBox függvényt, amely beolvas egy stringet egy felbukkanó ablak segítségével. Az InputBox használata: string s = Interaction.InputBox(kérdés,felbukkanó ablak felirata,alapértelmezett válasz,x koordináta, y koordináta); Meg kell tehát adnunk a kérdést, amit feltesz a felhasználónak, az ablak feliratát, az alapértelmezett választ (ez eddig három string típusú paraméter), majd a megjelenő ablak x és y koordinátáját. A beolvasás addig tart, amíg a felhasználó a 0 értéket nem adja meg (egyébként lehet ez a felbukkanó ablak alapértelmezett paramétere is). Ennek megoldásához használhatunk hátultesztelő ciklust, ugyanis egy adatot mindenképpen be kell olvasnunk, és akkor kell folytatnunk, ha a beírt érték nem 0. Mivel a nulla értéket nem szeretnénk eltárolni a listában, szükségünk van egy elágazásra, ami kiszűri ezt. Továbbá egy elágazással meg kell vizsgálnunk, hogy a szám páros-e, azaz kettővel osztva nullát ad-e maradékul. Az oszthatósági vizsgálathoz a % (maradék) műveletet használhatjuk: if (szam % 2 == 0) Ha a szám kettővel való osztási maradéka egyenlő nullával, az azt jelenti, hogy páros. 37
38 Nézzük ezek után a kódot: int szam; do string s = Interaction.InputBox("Kérem a következı számot:","adatbevitel","0",100,100); szam = int.parse(s); if (szam!=0) if (szam % 2 == 0) listbox1.items.add(s); else listbox2.items.add(s); while (szam!=0); A ciklusváltozót mindenképpen deklarálnunk kell a ciklus előtt, mert különben nem használhatjuk a ciklusfeltételben. Amennyiben a szám páros, az egyik, ha nem, akkor a másik listához adjuk hozzá. FELADATOK 4.6.Írjunk programot, amely egy dolgozat pontszáma alapján megadja a jegyet számmal és szövegesen is! A ponthatárok: 0-10: 1, 11-20: 2, 21-30: 3, 31-40:4, és 41-50: 5. Egy címkére írjuk ki az eredményt! 4.7.Írjuk ki egy ListBoxba 0-tól 100-ig a számokat, 4-esével! 4.8.Írj programot, amely egy bekér egy évszámot, és megmondja róla, hogy szökőév-e! (Szökőév az, amely néggyel osztható és százzal nem, vagy néggyel és négyszázzal is osztható.) A választ itt is egy címkére írjuk! 38
39 ÖSSZETETT ADATTÍPUSOK: REKORD, TÖMB, FELTÖLTÉS SZÖVEGFÁJLBÓL Ennek a témakörnek az a célja, hogy az adatbevitel gyorsítása érdekében szöveges fájlokból olvass be adatokat, illetve nagy mennyiségű eredményt hasonló formában állíts elő. Közben megtanulhatod az összetett adattípusok kezelését, és képes leszel rekordokból álló tömböket feltölteni, adataikat megjeleníteni. ELMÉLETI ÖSSZEFOGLALÓ Eddig egyszerű típusú adatokkal foglalkoztunk, amelyek egyetlen adatot képesek eltárolni (kivéve a string, ami tulajdonképpen karakterek sorozata). Gyakran szükségünk van arra, hogy több összetartozó adatot egy helyen tároljunk, nem pedig külön változókban. Ennek egyik eszköze a struktúra (rekord) típus. A struktúra több, egyszerű típusú adatból épül fel, amelyeket mezőknek nevezünk. Struktúra típus deklarációja: struct típus_név public mezı_típus 1 mezı_név 1 ; public mezı_típus 2 mezı_név 2 ; public mezı_típus n mezı_név n ; A public kulcsszó azt jelenti, hogy a mező értéke a program bármelyik részéből elérhető, módosítható. Ennek elhagyása vagy a private kulcsszó használata esetén csak a struktúrába írt metódusok használhatnák ezeket a mezőket. Struktúra típusú változó deklarációja: típus_név változó_név; Példa: tároljuk egy rekordban egy ország nevét és területét! struct Rekord public string orszag; public int terulet; Struktúra típusú változó deklarációja: Rekord x; Értékadás a rekord mezőinek: mezőszelektor (.) használatával: x.terulet = 15000; A rekord típusú változó is csak egyetlen, logikailag összetartozó adatcsoportot tud tárolni. Több, azonos típusú adat tárolására a tömböt használhatjuk. Az egydimenziós tömböt vektornak, a kétdimenziós tömböt mátrixnak nevezzük. 39
40 Vektor típusú változó deklarációja: bázistípus[] változó_név; Ez még csak egy üres hivatkozás, nem használható, amíg helyet nem foglalunk az elemek számára! Helyfoglalás MAX db elem számára: változó_név = new bázistípus[max]; Ilyenkor az elemek sorszáma (indexe) 0-tól MAX-1-ig terjedhet! Példa (deklaráció és helyfoglalás egyben): const int MAX = 100; int[] v = new int[max]; A tömb elemeinek elérése (indexelés): v[2] = 24; Elemek bejárása for ciklussal: for (int i=0; i<max; i++) listbox1.items.add(v[i]); Struktúra típus deklarációja: struct Rekord public string orszag; public int terulet; Struktúra típusú tömb deklarációja és helyfoglalás: Rekord[] v = new Rekord[MAX]; Értékadás indexeléssel és mezőszelektorral: v[i].terulet = 15000; Beolvasás szövegfájlból: 1. A using lista kibővítése: using System.IO; 2. Fájl létezésének ellenőrzése: if (File.Exists(fájlnév)) 3. Fájltípus deklarációja és a fájl megnyitása: StreamReader f = File.OpenText(fájlnév); 4. Olvasás, amíg nincs vége a fájlnak: while (!f.endofstream) 5. Sorok beolvasása (a cikluson belül): string sor = f.readline(); 6. Fájl bezárása: f.close(); 40
41 Kiírás szövegfájlba: Különbségek a beolvasáshoz képest: 1. Fájltípus deklarációja és a fájl megnyitása: StreamWriter f = File.CreateText(fájlnév); 2. Nem kell használni az f.endofstream tulajdonságot, mert íráskor nem a fájl végéig írunk, hanem addig, amíg el nem fogy a kiírandó adatmennyiség. 3. Egy sor kiírása: f.writeline(sor); 4. A fájl bezárása kötelező! f.close(); BEOLVASÁS SZÖVEGFÁJLBÓL, KIÍRÁS SZÖVEGFÁJLBA 5.1.Írjuk át a 4.5. feladatban készített programot, hogy ne billentyűzetről, hanem a mellékelt szamok.txt fájlból olvassa be az adatokat, és azokat válogassa szét párosakra és páratlanokra! Az alkalmazás kezelőfelületén nem sokat kell változtatnunk, csak egy új gombot kell felvennünk. Használjunk a feladat megoldásához tömböt! ábra Beolvasás fájlból, szétválogatás, kiírás fájlba Az eddigi programjainkban a változókat mindig az eseménykezelő metódusokban deklaráltuk, így ezek csak azokban a metódusokban voltak használhatók. Az így deklarált változókat nevezzük lokális változóknak. Ezzel szemben a tömböt, a tömb maximális és tényleges elemszámát célszerű úgy felvenni, hogy minden eseménykezelő metódusból el lehessen érni, azaz globális változóként kell őket deklarálni. A globális változókat minden eseménykezelő eljárás elé, a public Form1() sor elé célszerű írni. const int MAX = 100; int[] szamok = new int[max]; int n; 41
42 A const kulcsszóval úgynevezett konstanst deklarálhatunk, amelynek egyszer adhatunk értéket, és azt az értéket a program során nem változtathatjuk meg. Mivel fájlból olvasunk be, nem tudjuk előre megmondani, hogy hány elemnek kell helyet foglalnunk. Ezért veszünk fel egy legfeljebb MAX elemű vektort, és az n értékét használjuk arra, hogy tudjuk, hány elem van valójában benne. A fájlból való beolvasáshoz a korábbi feladat hátultesztelő ciklusát elöltesztelővé kell átalakítanunk. Fontos azt is megjegyezni, hogy a szövegfájlból mindig string típusú értéket tudunk beolvasni (hasonlóan az InputBoxhoz), ezért konvertálnunk kell. n = 0; string fnev = "szamok.txt"; if (File.Exists(fnev)) StreamReader f = File.OpenText(fnev); while (!f.endofstream && n<max) string sor = f.readline(); szamok[n] = int.parse(sor); if (szamok[n] % 2 == 0) listbox1.items.add(sor); else listbox2.items.add(sor); n++; f.close(); button2.enabled = true; else MessageBox.Show("A megadott fájl nem létezik."); A szövegfájlokat a projekt könyvtárában a bin\debug mappába kell bemásolnunk, mert különben nem találja meg a program futtatásakor. Természetesen a fájlneveket megadhatjuk útvonallal együtt is, ekkor azonban fennáll a veszélye, hogy a program más mappába helyezésekor nem fogja megtalálni a szövegfájlt. A fent látható kódrészlet feltölti a tömböt a beolvasott számokkal, és egyúttal szétválogatja őket, és elhelyezi a ListBoxokban. A while ciklus feltételét azért bővítettük ki az n<max feltétellel, mert így akkor sincsen probléma, ha a fájlban több elem van, mint amennyinek helyet foglaltunk. Az n értékét pedig azért növeljük a beolvasás után, mert a tömb indexelése 0-tól indul, másrészt pedig így ez a változó a beolvasott elemek tényleges darabszámát jelenti. A fájlba íráskor láthatjuk is, hogy 0-tól n-1-ig kell végigmennünk az indexeken. Célszerű a második gombot letiltani, hiszen nincs mit fájlba írni, ha még nem olvastuk be az adatokat. A fájl bezárása után azonban már engedélyezhetjük a nyomógombot. A fájlba írás lényegesen egyszerűbb. Egy számláló ciklussal végigmegyünk a tömb elemein, és amelyik páros, azt kiírjuk a szövegfájlba. Nagyon fontos, hogy a végén ne felejtsük el lezárni a fájlt, mert különben lehetnek olyan sorok, amelyek nem kerülnek bele! 42
43 Amennyiben nem adunk meg útvonalat, az elkészült szövegfájlokat ugyanúgy a bin\debug mappában kell keresnünk, mint a beolvasáskor. string fnev = "paros.txt"; StreamWriter f = File.CreateText(fnev); for (int i = 0; i < n; i++) if (szamok[i] % 2 == 0) f.writeline(szamok[i]); f.close(); MessageBox.Show("A páros számokat fájlba írtam."); Amennyiben a tömbünk valamennyi eleme fel lenne töltve adatokkal, az elemek feldolgozásához használhatnánk a foreach ciklust, ami mindenképpen végigmegy az egész tömbön. Ciklusváltozóként nem indexet, hanem olyan típusú változót használ, mint amilyen típusú elemekből a tömb áll, és a futás során azzal lehet hivatkozni a tömb elemeire. A fenti ciklust így írhatnánk foreach segítségével: foreach (int elem in szamok) if (elem % 2 == 0) f.writeline(elem); 43
44 STRUKTÚRA ÉS REKORD EGYÜTT 5.2.Nyissuk meg a 2.1. feladatban megtervezett programot, és írjuk meg a fájlból történő beolvasást! Az eredményt jelenítsük meg a ListBoxban! A mellékelt szövegfájlban (adat.txt) az országok neve és területe külön sorban található ábra Beolvasott adatok megjelenítése A feladat nagyon hasonlít az előzőhöz. Először deklaráljuk a struktúra típust, és a tömböt, természetesen mindegyiket globálisként. struct Rekord public string orszag; public int terulet; const int MAX = 100; Rekord[] v = new Rekord[MAX]; int n; A feltöltés is hasonlóan megy, két apró különbséggel. Az egyik, hogy a fájl nevét egy szövegdobozba írja a felhasználó, és amennyiben ezt elmulasztja, akkor figyelmeztetjük erre. A másik, hogy mivel a tömb elemei rekordok, ezért külön-külön kell beolvasni a két mező értékét, így két ReadLine() kell a ciklusba. A beolvasott adatokat írjuk ki a ListBoxba! 44
45 n = 0; listbox1.items.clear(); if (textbox1.text!= "") if (File.Exists(textBox1.Text)) StreamReader f = File.OpenText(textBox1.Text); while (!f.endofstream && n<max) v[n].orszag = f.readline(); string sor = f.readline(); v[n].terulet = int.parse(sor); listbox1.items.add(v[n].orszag + " területe: " + v[n].terulet.tostring()); n++; f.close(); else MessageBox.Show("A megadott nevő fájl nem létezik!"); else MessageBox.Show("Nem adott meg fájlnevet!"); A kiírásnál a mintában az összetartozó adatokat egy sorba írjuk ki: az ország neve mellett mindjárt a területét is megjelenítjük. Mivel a + operátor szám és szöveg típusú adatokat közvetlenül nem tud összefűzni, ezért a területet string típusúvá kell konvertálni a ToString() függvénnyel. FELADATOK 5.3. Bővítsük ki az 5.1 feladatban megírt programot, úgy, hogy beolvasás közben számolja ki a páratlan számok átlagát! Az eredményt írjuk ki a képernyőre! 5.4. Egy szövegfájlban (aru.txt) gyümölcsök nevei vannak, a következő sorban található, hogy hány kg áll rendelkezésre belőlük, a harmadikban pedig az ár szerepel. Olvassuk be ezeket egy struktúrából álló tömbbe, majd számoljuk ki a készlet összértékét (azaz adjuk össze a kg*ár értékeket)! Az eredményt jelenítsük meg a képernyőn! 5.5. Az 5.2 feladat programjában írjuk meg azt a programrészletet, amely kiszámítja az országok területének átlagát! Az eredményt itt is írjuk ki! 45
46 ALAPVETŐ PROGRAMOZÁSI TÉTELEK MEGVALÓSÍTÁSA Ennek a témakörnek az a célja, hogy megismerkedj az alapvető programozási tételekkel, illetve megtanuld, hogyan kell őket megvalósítani C# nyelven. Közben gyakorolhatod, hogyan kell feltételeket megfogalmazni, illetve alkalmazni az eddig tanult vezérlési szerkezeteket. ELMÉLETI ÖSSZEFOGLALÓ Vannak olyan típusfeladatok, amelyeket igen hasonlóan kell megoldani. Például: átlagszámítás egy sorozat elemein, bizonyos feltételnek megfelelő elemek megszámolása, legkisebb / legnagyobb elem meghatározása, adott elem megkeresése. Ezen feladatok megoldására általános algoritmusokat adunk, amelyeket programozási tételeknek nevezünk. A továbbiakban legyen: v: tetszőleges elemtípusú vektor n: egész (a vektor tényleges, valódi elemszáma) adott tulajdonság: a feladat szövegéből következő logikai függvény (pl. egy adott szám páros, nagyobb mint 1000, egyenlő egy konkrét értékkel, stb.) A vektor elemtípusa lehet struktúra is, de a tételt mindig a rekord egy mezőjére kell felírni. Összegzés tétele (algoritmus): Adott a v[0..n-1] vektor, amelynek elemein értelmezhető az összeadás művelete. Számítsuk ki a vektor elemeinek összegét (ossz változó)! ossz := 0 ciklus i:=0-tól n-1-ig ossz := ossz + v[i] ciklus vége. Mire kell figyelni a kódolásnál? A kezdőérték nem biztos, hogy 0. C# nyelvben a tömb indexelése 0-tól indul. Összegzés tétele (kód): int ossz = 0; for (int i=0; i<n; i++) ossz += v[i]; Alkalmazás: átlagszámítás összegzés után! double atl = ossz / (double)n; 46
47 Megszámlálás tétele (algoritmus): Adott a v[0..n-1] vektor, amelynek elemein értelmezhető az adott tulajdonságfüggvény. Számoljuk meg, hogy a vektorban hány darab, az adott tulajdonságnak megfelelő elem található (db változó)! db := 0 ciklus i:=0-tól n-1-ig ha v[i] adott tulajdonságú akkor db := db + 1 ciklus vége. Mire kell figyelni a kódolásnál? Mindig adjunk 0-t kezdőértéknek! C# nyelvben a tömb indexelése 0-tól indul. Megszámlálás tétele (kód): Legyen a tulajdonságfüggvény, hogy az adott elem páros! int db = 0; for (int i=0; i<n; i++) if (v[i]%2 == 0) db++; Konkrét feladat esetén csak az elágazás feltételét kell másra kicserélni! Minimum- és maximumkiválasztás tétele (algoritmus): Adott a v[0..n-1] vektor, amelynek elemein értelmezhető a kisebb / nagyobb reláció. Válasszuk ki a vektor legkisebb / legnagyobb elemét! A ind változó jelentse a minimum helyét, azaz indexét! ind := 0 ciklus i:=1-tıl n-1-ig ha v[i]<v[ind] akkor ind := i ciklus vége. Mire kell figyelni a kódolásnál? C# esetén 0 kezdőértékkel kell indulnunk. Maximumkiválasztásnál a reláció megfordul. 47
48 Minimum- és maximumkiválasztás tétele (kód): int ind = 0; for (int i=1; i<n; i++) if (v[i]<v[ind]) ind = i; Struktúrából álló tömb esetén a feltételvizsgálat például így nézhet ki: if (v[i].terulet<v[ind].terulet) Lineáris keresés tétele (algoritmus): Adott a v[0..n-1] vektor, amelynek elemein értelmezhető az adott tulajdonságfüggvény. Keressük meg a vektor első adott tulajdonságú elemét! Az l logikai változó legyen igaz, ha sikerült találni ilyen elemet, és ekkor az i tartalmazza az első adott tulajdonságú elem indexét. i := -1; l := hamis ciklus amíg (i<n-1) és (NEM l) i := i+1 ha v[i] adott tulajdonságú akkor l := igaz ciklus vége. Mire kell figyelni a kódolásnál? C# esetén -1 kezdőértékkel kell indulnunk, és a ciklusfeltételben i<n-1 szerepel. Ennek oka, hogy a ciklusba belépés után azonnal növeljük az i értékét, és csak utána vizsgáljuk meg a feltételt. Így mindig azon az elemen állunk meg, amelyik először rendelkezik az adott tulajdonsággal. C# nyelvben a tagadás a! operátor segítségével írható. (NEM l kódolása:!l) Lineáris keresés tétele (kód): Legyen a tulajdonságfüggvény, hogy az adott elem páros! int i = -1; bool l = false; while (i<n-1 &&!l) i++; if (v[i]%2 == 0) l = true; Konkrét feladat esetén csak az elágazás feltételét kell másra kicserélni! 48
49 PROGRAMOZÁSI TÉTELEK MEGVALÓSÍTÁSA 6.1.Írjuk át az 5.1. feladatban készített programot, és egészítsük ki a programozási tételek megvalósításával! Az eredményeket minden feladatnál írjuk ki a képernyőre MessageBox segítségével! Az alkalmazás kezelőfelületén nem sokat kell változtatnunk, csak néhány új gombot kell felvennünk. Vegyük fel ezeket a gombokat, a feliratukat állítsuk be az alábbi ábrán látható módon! ábra Kiegészítve programozási tételekkel Ha végeztünk az alkalmazás kezelőfelületének átalakításával, a négy gombhoz meg kell írnunk a megfelelő eseménykezelő eljárást. Melyik feladatot melyik programozási tétel segítségével kell megoldani? Az első feladat az elemek átlagának meghatározása. Mivel ehhez össze kell adni az elemeket, majd a végén el kell osztani őket a darabszámmal, ezért a feladathoz az összegzés tétele használható. ÖSSZEGZÉS TÉTELE Ebben a feladatban nincsen semmi specialitás, az elemeink egyszerű egész számok, és tömbben vannak. Az alap algoritmuson nem kell sokat változtatni. Mivel az átlag mindenképpen valós érték, ezért célszerű már az összegzéshez is valós számként felvennünk, így megússzuk a végén a típuskényszerítést. Az összegzés tételét felhasználva az átlagszámítás, a szamok nevű tömbre felírva: double atlag = 0; for (int i = 0; i < n; i++) atlag += szamok[i]; atlag = atlag / n; 49
50 Az atlag += szamok[i] egyenértékű az atlag = atlag + szamok[i] utasítással. Természetesen gondoskodnunk kell arról is, hogy a kiszámított érték megjelenjen a képernyőn. Ez történhet egy címkére kiíratva, illetve üzenetablakba, az alábbi módon: MessageBox.Show("A tömb elemeinek átlaga: " + atlag.tostring()); MEGSZÁMLÁLÁS TÉTELE A megszámlálás tételének megvalósításában egyetlen dolgot kell mindig a feladat szövegéhez igazítanunk: a v[i] adott tulajdonságú feltételét kell lecserélnünk a kívánt logikai kifejezésre. Mivel ebben a feladatban a 80-nál nagyobb elemek darabszámát kell megszámolnunk, ezért a feltétel a következőképpen néz ki: szamok[i] > 80. Lássuk tehát a megszámlálás megvalósítását: int db = 0; for (int i = 0; i < n; i++) if (szamok[i] > 80) db++; MessageBox.Show("A 80-nál nagyobb elemek száma: " + db.tostring()); A db++ egyenértékű a db = db + 1 utasítással. A kiíratás az előzőhöz hasonlóan történik. MINIMUM- ÉS MAXIMUMKIVÁLASZTÁS TÉTELE Mivel a két programozási tétel között mindössze egyetlen relációs jel állása a különbség, ezért együtt valósíthatjuk meg őket. Jegyezzük meg, hogy a tételben nem a maximum értékét, hanem a helyét (indexét) tartjuk nyilván, így könnyebben tudjuk más típusú értéket tartalmazó tömbre is alkalmazni azt. Írjuk meg a harmadik gomb eseménykezelőjét, úgy, hogy egyetlen ciklusban meghatározzuk egyszerre a minimum és a maximum értékét is! int min = 0; int max = 0; for (int i = 1; i < n; i++) if (szamok[i] < szamok[min]) min = i; if (szamok[i] > szamok[max]) max = i; 50
51 Mint látható, elegendő volt felvenni még egy plusz változót, és a ciklusban megismételni az elágazást, ellenkező relációs jellel. Mivel, a min és a max változók a legkisebb és legnagyobb elem helyét (indexét) jelzik, ezért kiíratáskor a szamok[min] és szamok[max] értékét kell kiíratni: MessageBox.Show("A legkisebb elem: " + szamok[min].tostring() + ", a legnagyobb elem: " + szamok[max].tostring()); Vajon mit jelenthet a minimum és a maximum akkor, amikor nem szám, hanem szöveges típusú adatokra alkalmazzuk? LINEÁRIS KERESÉS Utolsó programozási tételünk a keresés. Az egyetlen nehézsége abból adódik, amit a megszámlálásnál már láttunk, hogy valamilyen tulajdonsággal rendelkező elemet kereshetünk vele. Ebben a feladatban viszont egy adott elemet kell megkeresnünk, amit előtte a felhasználótól olvasunk be. Emlékeztető: a beolvasásra alkalmas lehet az InputBox függvény, amit a Visual Basictől vettünk kölcsön. int adott = int.parse(interaction.inputbox("kérem az adott elemet:", "Adatbevitel", "", 50, 50)); Amennyiben megvan az adott szám, a keresés könnyen felírható. Akkor kell megállnunk, ha a tömb éppen vizsgált eleme egyenlő a beolvasott értékkel, ezért a ciklusfeltételbe ezt kell írnunk. int i = -1; bool l = false; while (i < n-1 &&!l) i++; if (szamok[i] == adott) l = true; A keresés eredményét csak akkor lehet kiírni, ha a logikai változó értéke igaz a ciklus lefutása után. Ezért minden lineáris keresés után meg kell vizsgálni az l értékét, és az annak megfelelő üzenetet kell kiíratni: if (l) MessageBox.Show("Az adott elem a tömbben van."); else MessageBox.Show("Az adott elem nincs a tömbben."); 51
52 PROGRAMOZÁSI TÉTELEK STRUKTÚRA TÍPUSÚ ADATOKRA 6.2.Nyissuk meg az 5.2. feladatban elkészített programot, és írjuk meg a többi gomb eseménykezelő metódusát, azaz a többi programozási tételt! Ügyeljünk arra, hogy az eredmények minden feladatnál jelenjenek meg a képernyőn! 19. ábra Itt az ideje tartalommal megtölteni az alkalmazás kezelőfelületét Emlékeztetőül a feladatban használt adattípusok és változók a következők: struct Rekord public string orszag; public int terulet; const int MAX = 100; Rekord[] v = new Rekord[MAX]; int n; Segítség a feladat megoldásához: Átlagszámítás: mivel a területek átlagát kell kiszámolni, ezért az összegzésben a v[i].terulet szerepel. A megszámlálásban a rádiógombokkal tudjuk kiválasztani, hogy mit is akarunk megszámolni, emiatt a feltétel kicsit bonyolultabb. Vagy az elsőt jelöltük be, és akkor a nél nagyobb területűeket számoljuk, vagy a másodikat és akkor a nél kisebb vagy azzal egyenlőket kell beleszámolnunk. Így a kicsit összetett feltétel: if ((radiobutton1.checked && v[i].terulet > ) (radiobutton2.checked && v[i].terulet <= )) 52
53 Az előző feladatban már csináltunk olyat, hogy minimumot és maximumot is kiválasztunk. Ebben a feladatban viszont a két lehetőség közül választani kell a kombinált lista segítségével. Ezért az első feladatban megírt elágazásokat egy újabb elágazásba kell beágyazni, az alábbi módon: if (combobox1.selectedindex == 0) // ide jön a minimumkiválasztás elágazása else // ide pedig a maximumkiválasztásé A lineáris keresésnél két dologra kell odafigyelni. Az egyik, hogy a felhasználó most szövegdobozba írja be a keresett ország nevét, ezért azt nem hagyhatja üresen, ezt egy elágazással meg kell vizsgálni. A másik, hogy amennyiben a jelölőnégyzetet bekattintjuk, akkor a megtalált ország sorát ki is kell jelölni a listában, a következőképpen: if (checkbox1.checked) listbox1.selectedindex = ind; A Kiírás gomb fájlba írja azon feltételeknek megfelelő országokat, amelyet valamelyik rádiógombbal kiválasztottunk. Így ugyanazt a feltételt kell használnunk, mint amit a megszámlálásnál: if ((radiobutton1.checked && v[i].terulet > ) (radiobutton2.checked && v[i].terulet <= )) FELADATOK 6.3.Írjuk át úgy a 6.2. feladatban megírt programot, hogy a rádiógombokkal kiválasztott feltételnek megfelelő ( nél nagyobb, illetve legfeljebb akkora területű) országoknak megfelelő sorokat egy másik ListBoxba is írja ki, az ábrán látható módon! ábra A 6.2 feladat, kiegészítve a feltételnek megfelelő kiválogatással 53
54 HIBÁK, HIBAÜZENETEK ÉS KEZELÉSÜK Ennek a témakörnek az a célja, hogy megismerkedj a programozás során elkövethető hibák fajtáival, ismerd és értsd meg a programozási környezet legfontosabb hibaüzeneteit, és képes legyél ezeket a hibákat kijavítani. Amennyiben a programod rosszul működik, tudd, hogy milyen eszközök állnak rendelkezésedre a hiba helyének és fajtájának felderítéséhez és javításához. ELMÉLETI ÖSSZEFOGLALÓ A programozás során óhatatlanul hibákat vétünk. Ezeket a hibákat két fő csoportra tudjuk osztani: Szintaktikai: formai hiba, azaz a programozási nyelv szabályainak figyelmen kívül hagyása. Szemantikai: tartalmi hiba, azaz egy rossz vagy rosszul kódolt algoritmus miatt a program nem vagy hibásan működik. Szintaktikai hibák: A fordítóprogram ezeket minden esetben kiszűri. A szintaktikai hibás program nem lefordítható, amíg mindent ki nem javítottunk benne. A fejlesztői környezet sok esetben felajánlja a hiba javítását. Leggyakoribb hibaüzenetek: karakter expected (Az adott helyről a megadott karakter ; hiányzik.) Cannot implicitly convert type típus1 to típus2 (Nem lehet automatikusan az egyik típusról a másikra konvertálni. Tipikus hiba, amikor string típusú értéket akarunk valamilyen szám típusú változónak értékül adni.) Operator op cannot be applied to operands of type típus1 and típus2 (Az adott műveleti jel nem alkalmazható az adott típusokra, például egy string és egy int típusú változó nem osztható el egymással.) Use of unassigned local variable változónév (Az adott változót úgy akartuk használni, hogy nem adtunk neki előtte értéket.) The name név does not exist in the current context (Leggyakrabban akkor fordul elő, amikor elgépelünk valamit: egy változó nevét, vagy kulcsszót, és a fordító számára az így keletkezett hibás név nem értelmezhető az adott blokkban.) Megjegyzés: a C# nyelvben blokknak nevezzük a programkód azon részét, amit a és jelek fognak közre. 54
55 Szemantikai hibák: A program formailag hibátlan, azonban mégsem működik jól. Hibajelenségek, és azok lehetséges okai: Nem a várt eredményt adja Rossz algoritmusból indultunk ki, vagy rosszul kódoltunk (például ciklusban nulláztunk, vagy osztottunk, amikor nem kellett volna). Nem fejeződik be a futása ( lefagy ) Elrontottuk a ciklusfeltételt, vagy nem változtatjuk a ciklusváltozó értékét a ciklusmagban, ezért végtelen ciklus az eredmény. Hibaüzenettel leáll a futása (kivételt dob, lásd a következő fejezetben) Nullával osztottunk, vagy szám helyett szöveget írtunk be. Hibakeresési eszközök: Lépésenkénti futtatás Az F11 billentyű lenyomásával a programot soronként, utasításonként futtathatjuk, és figyelhetjük az eredményeket. Lokális változók figyelése (Locals) Figyelemmel tudjuk kísérni a lokális változók aktuális értékét, így láthatjuk, melyik lépésben válik rosszá az eredmény. Töréspont beiktatása A program futását egy adott helyen megszakíthatjuk, ahol úgy sejtjük, hogy elromlik. Hibás program javítási lépései: 1. Töréspont beiktatása Oda célszerű elhelyezni, ameddig szerintünk jól működik a program. 2. Lépésenkénti futtatás A törésponttól indítva futtassuk lépésenként a programot. 3. Lokális változók figyelése (Locals) A lépésenkénti futtatás közben kísérjük figyelemmel a lokális változók aktuális értékét. 4. Javítás, és azonnali folytatás A hiba kijavítása után használhatjuk az Edit and Continue funkciót, azaz a programot a teljes újrafordítás nélkül kipróbálhatjuk, és rögtön láthatjuk a javítás eredményét. 55
56 SZINTAKTIKAI HIBÁK JAVÍTÁSA 7.1.Nyissuk meg a Szintaktikai_Hibas nevű projektet! Próbáljuk meg lefordítani! Azt tapasztaljuk, hogy a fejlesztői környezet négy szintaktikai hibát jelez ennek során, pedig a program ennél jóval többet tartalmaz. Javítsuk ki a hibákat lépésről lépésre! A hibajavítás aranyszabálya, hogy lehetőleg egyszerre csak egy hibát próbáljunk meg javítani, és minden javítás után fordítsuk újra a programot! Előfordulhat ugyanis, hogy egy hiba javítása több hibaüzenettől is megszabadít bennünket. 1. Empty character literal Az első hiba abból adódik, hogy a textbox1.text tulajdonsága string típusú, mi pedig egy üres karakterrel ('') próbáljuk meg összehasonlítani, ilyen pedig nem is létezik. Cseréljük ki üres sztringre (dupla idézőjel: "")! 2. Syntax error, '(' expected Ez a hibaüzenet elég egyértelmű, egy nyitó zárójelet hiányol az if kulcsszó után. Írjuk oda! 3. Invalid expression term 'else' Ha figyelmesebben megnézzük a kódot, az else kulcsszót változónévként használjuk, és két sorral lejjebb az is kiderül, hogy eredetileg elso -t akartunk írni. Javítsuk ki a hibás változónevet! 4. Operator '&&' cannot be applied to operands of type 'string' and 'bool' Ezt az üzenetet egy nehezebben felderíthető hiba okozza. Szó szerint azt jelenti, hogy az ÉS logikai műveletet nem lehet alkalmazni string és bool típusú operandusok esetén. Jelen esetben az üres string ("") és a (textbox2.text!= "") kifejezések között szeretné ezt a műveletet elvégezni. Ennek oka, hogy a textbox1.text után egyetlen darab egyenlőségjel szerepel, ami értékadás és nem egyenlőségvizsgálat. Ráadásul itt az a feltétel kell, hogy egyik szövegdoboz sem üres. Írjunk egy felkiáltójelet az egyedül álló egyenlőségjel elé! 5. Cannot implicitly convert type 'int' to 'string' A két változó (elso és masodik) összege egész típusú, és egy string típusú változónak szeretnénk értékül adni. Cseréljük az eredm változó típusát int-re! 56
57 6. Cannot implicitly convert type 'int' to 'string' Újból felbukkant a hibaüzenet, a következő sorban. Most viszont az eredm változót kell string típusúvá konvertálnunk, hogy értékül tudjuk adni a textbox3 Text tulajdonságának. Tegyük meg, írjuk utána a.tostring() függvényhívást! 7. The name 'MassageBox' does not exist in the current context Az utolsó hiba egyszerű elgépelés: javítsuk ki a hibásan írt szót MessageBox-ra! A javítások után a program már lefordítható és futtatható. Látható, hogy egy rövid kis programban is rengeteg apró hibát lehet véteni. Kis odafigyeléssel, és a fordító üzeneteinek értelmezésével azonban ezeket könnyedén ki tudjuk javítani. SZEMANTIKAI HIBÁK JAVÍTÁSA Sokkal nehezebb dolgunk van, ha olyan hibát kell kijavítanunk, amelynek okát nem tudjuk, vagy amelyről a fordító nem tud bővebb információt adni. Ha a program lefordítható, de nem azt csinálja, amit szeretnénk, vagy nem csinál semmit, akkor nincs más választásunk, mint hogy megpróbáljuk kitalálni, mi lehet a baj. A továbbiakban egy ilyen programot fogunk kijavítani. 7.2.Nyissuk meg a Szemantikai_Hibas nevű projektet! A program egy számokat tartalmazó vektort tölt fel szövegfájlból, és kiszámolja a számok átlagát. Bár a program lefordítható, a fordítás során egy figyelmeztetés jelenik meg, amely a programban található egyik súlyos szemantikai hibára próbálja felhívni a figyelmünket. Fordítsuk le, és futtassuk a programot. Kattintsunk a Beolvasás fájlból gombra! Kis várakozás után a program elszáll, azaz kivételt dob. Kezdjük el kijavítani a programot! 1. NullReferenceException was unhandled Az objektumhivatkozás nincs beállítva semmilyen objektumpéldányra. Ha magyar nyelvű Windows verziót használunk, akkor ez a fenti hibaüzenet is magyarul jelenik meg. A hibát a szamok[n] tömbelemre való hivatkozás okozza. Ha jobban megnézzük, a tömböt deklaráltuk, de nem inicializáltuk. Pótoljuk, bővítsük ki a deklarációt: int[] szamok = new int[max]; Így a figyelmeztetés is eltűnik. 2. ObjectDisposedException was unhandled Nem lehet bezárt TextReader objektumból olvasni. Elég egyértelmű a hibaüzenet: bezárjuk a fájlt, miközben olvasunk belőle, mivel az f.close() a cikluson belülre került. Helyezzük át a cikluson kívülre! 57
58 3. FormatException was unhandled Nem megfelelı a bemeneti karakterlánc formátuma. Ez olyankor fordulhat elő, amikor a beolvasott adatot nem tudja átalakítani szám formátumúra. Nézzük meg a lokális változók értékét mutató Locals ablakot! A sor változó értéke 12e, amit nem tud egész számmá átalakítani és a tömbbe tenni. Javítsuk ki a hibás értéket a szövegfájlban! 4. IndexOutOfRangeException was unhandled Az index a tömb határain kívülre mutatott. Vigyük az n változó fölé az egérmutatót valahol a kódban! Láthatjuk, hogy n=10 esetén áll le a program ezzel a hibaüzenettel. A hiba oka az, hogy a MAX értéke mindössze 10, így a tömb indexei 0 és 9 közé eshetnek, így a 10. elemre már nem tudunk hivatkozni. Növeljük meg a MAX értékét 100-ra! Ezekkel a módosításokkal a beolvasás már le tud futni. Próbáljuk ki az átlagszámítást! 5. A gombra kattintva nem történik semmi. Ezek a legnehezebben felderíthető hibák. Helyezzünk el töréspontot az átlagszámítás elejére és futtassuk lépésenként a programot! Hamar rájöhetünk, hogy a for ciklus feltétele hibás, és ezért végre sem hajtja a ciklusmagot. Cseréljük ki a feltételt i<n -re! 6. Most már fut az átlagszámítás, de a ciklus minden lépésében kiír egy átlagot, ami ráadásul nem is tűnik törtszámnak. A kiíratást tegyük a cikluson kívülre! 7. A program látszólag jól működik, de a kiírt átlag kevesebb, mint a valódi átlag. Ezt persze nehéz észrevenni, így könnyen maradhat hiba a programunkban. Ha jobban megnézzük, akkor a cikluson belül osztunk az n értékével, ami semmiképpen nem helyes. Töröljük ezt ki, és a cikluson kívül osszunk vele: atlag /= n; 8. Már majdnem minden jó, de egyetlen apró, tényleg nehezen felderíthető hiba maradt a programban. Ha töréspontot teszünk a programban az átlagszámításhoz, és megnézzük a tömb tartalmát, rájöhetünk, hogy a 0. elem üresen maradt benne. Ennek oka, hogy feltöltéskor a ciklusban rossz helyen növeljük az n értékét. Tegyük az utasítást a ciklusmag végére! Kijavítottunk minden hibát a programban, az eredmény most már helyesen 65,65. 58
59 ÖSSZETETT FELADAT 7.3.Nyissuk meg a Harmadik_Hibas nevű projektet! A program úgy sorsol véletlenszerűen öt számot 1 és 90 között, hogy azok között ne legyen két egyforma érték. Vegyesen találhatók benne szintaktikai és szemantikai hibák is. Keressük meg és javítsuk ki ezeket! A programban szerepel véletlenszám-generálás is. Ehhez először létre kell hozni egy Random típusú objektumot: Random r = new Random(); Majd utána generálni kell a következő véletlen számot adott alsó és felső határ között, az alábbi módon: int x; x = r.next(also_hatar, felso_hatar+1); FELADATOK 7.4.Nézz utána az Interneten vagy a Súgóban, hogy a 7.2. feladatban említetteken kívül milyen kivételek (Exception) fordulhatnak elő a programok futása során! Szedj össze párat! 59
60 KIVÉTELEK KEZELÉSE, ELLENŐRZÖTT ADATBEVITEL Ennek a témakörnek az a célja, hogy megtanulj olyan nyelvi elemeket, melyeknek segítségével a hibás adatbevitelből eredő hibákat ki tudod küszöbölni. Tudd kezelni, ha a rossz formátumú adatot ad meg a felhasználó, illetve kényszeríteni tudod arra, hogy csak bizonyos feltételnek megfelelő értéket írhasson be. ELMÉLETI ÖSSZEFOGLALÓ Egy hibátlannak tűnő programot is ki lehet akasztani. Kivételről akkor beszélünk, amikor váratlan, előre nem látható hiba történik a programban. Ilyen hiba lehet: programozási (szemantikai hiba), vagy hardveres (például nem írható vagy olvasható egy fájl), vagy szoftveres (például nem megfelelő formátumú adatbevitel vagy bemenő fájl, nullával osztás). Tipikus kivételek (ld. előző fejezet): NullReferenceException: Az objektumhivatkozás nincs beállítva semmilyen objektumpéldányra. (Egy objektumot deklaráltunk, de még nem hoztuk létre a memóriában. ) ObjectDisposedException: Nem lehet bezárt TextReader objektumból olvasni. (Bezárt fájlból próbáltunk olvasni.) FormatException: Nem megfelelő a bemeneti karakterlánc formátuma. (Ez a leggyakoribb hiba, rossz formátumú adatot olvas be a program, és nem tudja átkonvertálni adott típusúra.) IndexOutOfRangeException: Az index a tömb határain kívülre mutatott. (Programozási hiba, rosszul indexelünk egy tömböt.) DivideByZeroException: Kísérlet történt nullával való osztásra. (Csak egész számok osztásakor fordulhat elő.) A kivételkezelés módja, hogy a veszélyes utasításokat egy kivételkezelő blokkba zárjuk, ami megpróbálja elkapni a dobott kivételeket. Így az esetlegesen bekövetkező hibajelenségeket biztonságosan le tudjuk kezelni. 60
61 Kivételek kezelése: try // problémás utasítások catch (kivételtípus 1 ) // hibaüzenet vagy hibajavítás catch (kivételtípus 2 ) // hibaüzenet vagy hibajavítás finally // olyan utasítások, amiknek még kivétel esetén is le kell futnia, nem kötelezı Példa nullával osztás kezelése: try int x = int.parse(textbox1.text); int y = int.parse(textbox2.text); int z = x / y; catch (DivideByZeroException) MessageBox.Show("Nullával nem oszthatunk!"); Több kivételt is kezelhetünk (lehet több catch blokk is). A módszer hátránya, hogy csak megadott kivételeket tudunk elkapni, nem tudunk mindenre felkészülni. Általános kivételkezelés (a kivétel típusának specifikálása nélkül): catch MessageBox.Show("Hiba történt a mővelet végrehajtása közben!"); Ellenőrzött adatbevitel: Erre akkor van szükség, ha rá akarjuk kényszeríteni a felhasználót, hogy megfelelő értéket írjon be. Például: osztályzat beolvasása esetén csak 1 és 5 közötti értéket fogadunk el. Megvalósítás: hátultesztelő ciklussal. do // adat beolvasása while (a_beolvasott_érték_rossz); 61
62 A módszer csak úgy alkalmazható, ha az adat beolvasását könnyen meg tudjuk ismételni (pl. InputBox feldobásával). Példa: osztályzat beolvasása do string s = Interaction.InputBox("Kérem a következı osztályzatot:", "Adatbevitel","0",100,100); szam = int.parse(s); while (szam<1 szam>5); Ügyeljünk a helyesen tagadott feltételre! Egészítsük ki kivételkezeléssel is! KIVÉTELEK KEZELÉSE FÁJLKEZELÉS SORÁN 8.1.Nyissuk meg a 6.1. feladatban elkészített régi projektünket! Alakítsuk át a felhasználói felületét az alábbi ábrán látható módon, hogy a szövegfájlok nevét szövegmezők segítségével meg tudjuk adni! ábra A programunk megújult felülete Ebben a programban két esetben lehet probléma: a fájlból beolvasás során, illetve ha az adott elem keresésénél nem adunk meg semmit, illetve rosszat (például betűt) írunk be. Először egészítsük ki a fájlból beolvasást! Fájlból beolvasásnál a File.Exists segítségével már kivédtük a leggyakoribb hibát, a nem létező fájl problémáját. Viszont számos más hiba is előfordulhat, ezért az a legszerencsésebb, ha egy általános kivételkezelő blokkot írunk, amellyel minden hiba kezelhető. A fájlból való beolvasást helyezzük át a try blokkba! Azért, hogy a változók deklarációjával kapcsolatban ne lehessen hatásköri probléma, a fájlváltozót még a try blokk előtt deklaráljuk! 62
63 StreamReader f = File.OpenText(fnev); try while (!f.endofstream && n < MAX) string sor = f.readline(); szamok[n] = int.parse(sor); if (szamok[n] % 2 == 0) listbox1.items.add(sor); else listbox2.items.add(sor); n++; Tulajdonképpen nem csináltunk mást, csak a fájlkezelő sorokat beágyaztuk egy kivételkezelő blokkba. Kivétel kezeléséhez legalább egy catch blokknak szerepelnie kell. A finally blokk opcionális, csak akkor kell, ha valamit mindenképpen végre szeretnénk hajtani, ha volt kivétel, ha nem. A catch blokkban nem kötelező kivételváltozót használni, ebben az esetben is minden kivételt el fog kapni. catch MessageBox.Show("Hiba történt a fájlból beolvasás során."); A finally blokkban azt szoktuk csinálni, hogy amennyiben a fájlt még nem zártuk be, akkor ezt megtesszük: finally if (f!= null) f.close(); Próbáljuk ki a programot! Ha a szamok.txt fájlt töltjük be, akkor a program kivételt dob, mert van a fájlban egy hibás érték. Amennyiben a szamok2.txt-t adjuk meg, hibátlanul lefut a beolvasás. Az adott elem keresésénél egyszerűbb a helyzet: tudjuk, hogy ha hibás formájú értéket írunk be, akkor el fog szállni a programunk, méghozzá FormatException kivétellel. Ezt kell csak lekezelnünk. A beolvasást és a lineáris keresést is bele kell foglalnunk a kivételkezelő blokkba. 63
64 Hatáskörrel és értékadással kapcsolatos gondokat okozna az alábbi kódrészlet: try int adott = int.parse(interaction.inputbox("kérem az adott elemet:", "Adatbevitel", "", 50, 50)); catch bool l = false; // ide jönne a lineáris keresés A két probléma: Az adott változót a try blokkban deklaráltuk, ezért csak ott lenne használható. A deklarációt így mindenképpen ki kéne vennünk a kivételkezelés elé. Ennél komolyabb probléma, hogy amennyiben kivétel történik, az adott változó nem kap értéket, és így nem tudjuk felhasználni a lineáris keresésben. Ezek miatt érdemesebb a teljes kódrészletet elhelyezni a try blokkban. try int adott = int.parse(interaction.inputbox("kérem az adott elemet:", "Adatbevitel", "", 50, 50)); bool l = false; int i = -1; while (l == false && i < n) i++; if (szamok[i] == adott) l = true; if (l) MessageBox.Show("Az adott elem benne van a tömbben."); else MessageBox.Show("Az adott elem nincs benne a tömbben."); catch (FormatException) MessageBox.Show("Hibás értéket adott meg!"); 64
65 ELLENŐRZÖTT ADATBEVITEL KIVÉTELKEZELÉSSEL 8.2.Írjunk programot, amely osztályzatokat olvas be, és azok átlagát számolja ki! Használjunk ellenőrzött adatbevitelt (csak 1 és 5 közötti számokat lehessen beírni), és végjelig történő beolvasást (végjel: 0)! Kivételkezeléssel oldjuk meg, hogy ne lehessen hibás értéket beírni! ábra Jegyek beolvasása Mivel az ellenőrzött adatbevitel és a végjelig történő beolvasás is hátultesztelő ciklust igényel, ezért ezeket egymásba kell ágyazni a programban. Célszerű a használt változókat ezek előtt deklarálni, és kezdőértéket adni nekik. double atl = 0; int db = 0; int szam; Az ellenőrzött adatbevitelt a prezentációban már lehetett látni, itt csak annyit kell rajta változtatni, hogy a 0-t is meg kell engedni, különben nem fog működni a végjelig történő beolvasás: do try string s = Interaction.InputBox("Kérem a következı osztályzatot:", "Adatbevitel", "0", 100, 100); szam = int.parse(s); catch (FormatException) MessageBox.Show("Hibás adatbevitel!"); szam = -1; while (szam < 0 szam > 5); A konvertálás miatt elhelyeztünk egy kivételkezelő blokkot is. Itt is a FormatException-t kapjuk el, és a szam változót azért állítjuk a catch blokkban -1-re, mert így az ellenőrzött adatbevitel ciklusfeltétele teljesül, és hibás adat beírása esetén újra beolvassa azt. Ágyazzuk ezt be egy végjelig történő beolvasásra, és utána számoljunk átlagot, majd jelenítsük meg a képernyőn az eredményt! 65
66 do // ide jön az ellenırzött adatbevitel if (szam!= 0) atl += szam; db++; while (szam!= 0); if (db!= 0) atl /= db; label1.text = "Átlag: " + atl.tostring(); Az átlag számolásakor figyelembe kell vennünk, hogy a végjelként beírt 0 érték ne számítson bele az átlagba, illetve ami fontosabb, hogy a darabszámot se növeljük vele. ÖSSZETETT FELADAT 8.3.Bővítsük ki egy korábbi programunkat, amely bekéri a másodfokú egyenlet a, b, c együtthatóit, és meghatározza az egyenlet gyökét vagy gyökeit, amennyiben ez lehetséges! A programban kezeljünk le minden lehetséges esetet (a=0, diszkrimináns negatív, nulla, illetve pozitív) is! Használjunk kivételkezelést! FELADATOK 8.4.Írj programot, amely bekér két egész számot és kiírja a hányadosukat és a maradékot! Használj kivételkezelést! A fenti feladatban a nullával osztást is kezelni kell! 66
67 PROJEKTFELADAT: PROGRAM KÉSZÍTÉSE Ennek a témakörnek az a célja, hogy az eddigi ismereteidet felhasználva csoportmunkában készítsd el egy nagyobb lélegzetű feladat megoldását. Ennek során a csapatmunka mellett el tudsz sajátítani jó pár olyan technikát, amely segít abban, hogy később, egy cégnél dolgozva képes legyél másokkal együttműködve megoldani nagyobb feladatokat. A FELADATOK KÖZÖS RÉSZE Amit a feladatok megoldásához mindenképpen meg kell valósítani: A program: el kell készíteni Visual C# Express programozási környezetben egy olyan programot, amely megoldja valamelyik kitűzött feladatot. Csak szintaktikailag tökéletes, fordításra és futtatásra alkalmas program elfogadható! Kezeljünk le minden olyan hibalehetőséget, amely problémát okozhat a program működésében, ahol szükséges, használjunk kivételkezelést! Bemutató (prezentáció), plakát vagy ismertető: egy olyan anyagot is el kell készíteni, amelyből nem csak a program funkciója, használatának módja derül ki, hanem amely marketing célokat is szolgál, azaz kiemeli a program használatának előnyeit, mintegy megpróbálja eladni a programot. Ennek segítségével kell a csoport valamelyik tagjának bemutatni a többieknek az elkészült produktumot. Fontos megjegyezni, hogy a mintaprogramok kizárólag támpontot nyújtanak a feladat elkészítéséhez, nem egy az egyben kell őket reprodukálni, az csak a minimum! Az elkészült programok legyenek szebbek és jobbak a mintaprogramoknál! SEGÍTSÉG A FELADATOK MEGOLDÁSÁHOZ A ListBox elemeit a listbox1.items[i] tömbhivatkozás segítségével érhetjük el, így végig tudunk menni az elemein, ahhoz, hogy fájlba tudjuk őket írni. A lista elemeinek számát a listbox1.items.count tulajdonság tárolja. Kommentet (megjegyzést) az alábbi két módon tudunk elhelyezni a programban: // ez csak a sor végéig tart /* ez tetszıleges számú soron keresztül tarthat */ 67
68 Annak érdekében, hogy könnyebben tudjatok együtt dolgozni, az egyes funkciókat érdemes paraméterek nélküli eljárásokba (metódusokba) írni. Ezek formája: private void Nev() // ide jön a metódus törzse Metódusok meghívása (pl. Click eseménykezelőből): Nev(); 68
69 LOTTÓSORSOLÁS ábra Lottósorsolást szimuláló program Készítsünk programot, amely lottósorsolást szimulál! Követelmények: Egy húzás alkalmával egy szám csak egyszer fordulhasson elő! Alapesetben ötöslottót szimuláljunk, azaz öt véletlenszámot állítsunk elő 1 és 90 között. Rendezzük a kihúzott számokat növekvő sorrendbe! A rendezési algoritmust az Interneten találhatjuk meg, például az alábbi címen: Minimumkiválasztásos rendezés: ( ) A kihúzott számokat az ábrán látható módon egy ListBoxba vezessük, amelynek tartalmát fájlba lehet menteni, illetve onnan betölteni és folytatni a sorsolásokat! 69
70 Továbbfejlesztési lehetőségek: Oldjuk meg, hogy lehessen választani ötös- és hatoslottó között (azaz lehessen hat véletlenszámot is sorsolni 1 és 45 között)! Lehessen választani, hogy melyik fájlba mentsen a program, illetve melyik állományból töltse be az adatokat! A program olvasson be számokat a felhasználótól, és ellenőrizze, hogy az utolsó sorsolás alkalmával hány számot sikerült eltalálni! 70
71 OSZTÁLYPÉNZ NYILVÁNTARTÁSA ábra Osztálypénz nyilvántartó program Készítsünk programot, amely segít az osztályfőnöknek az osztálypénzt nyilvántartani! Követelmények: Kétféle tranzakciót engedjünk meg: be- és kifizetést. Ne engedjünk meg olyan kifizetést, amelynek nincsen fedezete! Minden tranzakcióhoz olvassuk be a nevet és az összeget. Ne engedjünk meg negatív számot, és kivételkezeléssel kezeljük a hibás adat bevitelét! A végrehajtott műveleteket az ábrán látható módon egy ListBoxba vezessük, aminek tartalmát fájlba lehet menteni, illetve onnan betölteni és folytatni a tranzakciók feldolgozását! Továbbfejlesztési lehetőségek: Tároljuk el a be- és kifizetések jogcímét, és dátumát is! A jogcímeket egy külön listából lehessen választani, amelyet tudunk bővíteni is! Lehessen választani, hogy melyik fájlba mentsen a program, illetve melyik állományból töltse be az adatokat! A program készítsen kimutatást, hogy az egyes tanulók összesen mennyit fizettek be! 71
72 PROJEKTFELADAT: PROGRAM BEMUTATÁSA Ennek a témakörnek az a célja, hogy útmutatást adjon a program bemutatásához, és megadja az értékelés szempontjait. A projektfeladat lezárásaként be kell mutatni az elkészült műveket. Az óra menete: a csapatok egymás után ismertetik a megoldásukat a többi csapattal, akik a tanártól kapott értékelőlapon pontozhatják a projektfeladatokat. Az óra végén vagy következő órára a pontszámok összesítésre kerülnek, és akkor kiderül, hogy az osztály melyik csapat programját értékelte a legjobbnak! A bemutató során nagyon figyeljünk az óránkra, ne lépjük túl a ránk eső időkeretet, mert akkor a többi csapattól vesszük el a bemutatás lehetőségét! Az értékelés szempontjai: Külső: mennyire szép, jól használható, intuitív a program kezelői felülete. Helyes működés: a program a lehetséges hibákat jól kezeli, jó eredményt ad minden esetben, nem lehet kiakasztani. Extrák: mennyire tért el a mintaprogramtól, illetve valósította meg a javasolt továbbfejlesztéseket. Itt lehet díjazni, ha a program nem pusztán másolata a mintaprogramnak, hanem attól jó értelemben eltérő, igényes, egyedi munka. Átlátható, szép kód: jól tagolt, követhető, megjegyzésekkel ellátott, tehát egy külső ember számára könnyedén továbbfejleszthető. Marketing: mennyire igényes, elegáns bemutatót, ill. plakátot terveztek a program népszerűsítésére, milyen volt az előadó, mennyire mutatta be a program értékeit, mennyire hangsúlyozta az erősségeket. 72
73 KONZOL ALKALMAZÁS KÉSZÍTÉSE Ennek a témakörnek az a célja, hogy megtanulj szöveges felületű (konzol) alkalmazást készíteni. Ezzel az alkalmazástípussal akkor érdemes foglalkozni, ha gyorsan, egyszerűen kell egy programot megírni, és nem a kezelőfelület a legfontosabb, hanem sokkal inkább a mögötte lévő kód, ami valamilyen jól körülhatárolható feladatot old meg. ELMÉLETI ÖSSZEFOGLALÓ Console Application: karakteres (szöveges) kezelőfelület, amelynek kezelése csak billentyűzettel történhet. Jellemzője a szekvenciális (soronkénti) programvégrehajtás, és a tömörebb programkód. Előnyei: egyszerűbb elkészíteni, a programkódra lehet koncentrálni, a felhasználói felület nem lényeges, gyorsabb a program végrehajtása, látássérülteknek könnyebben használható programokat lehet vele írni. Hátrányai: bonyolultabb kezelhetőség, nehéz szép programot készíteni, korlátozott lehetőségek. Konzol alkalmazás "üres" kódja: namespace ConsoleApplication1 Névtér class Program Osztály static void Main(string[] args) Főprogram Névtér: olyan egység a programban, amelyen belül nem lehet két egyforma nevet használni. Hivatkozás a névtér egy nevére az alábbi módon történik: névtér.név (például System.IO.File). A rövidítés érdekében a gyakran használt névtereket felvehetjük egy using kezdetű sorral például az alábbi módon: using System.IO; Program osztály: minden felhasznált típus, változó, metódus a Program osztályba kerül. Ezek a főprogram szempontjából globálisak lesznek (minden metódusból, illetve a Main metódusból is használhatók). A static kulcsszót eléjük kell írni! Main metódus (főprogram): a programot alkotó utasításokat kell ide írni. Ezeket az utasításokat egymás után hajtja végre. A Main metódus lefutása után vége a programnak. Egy sor beolvasása billentyűzetről: string s = Console.ReadLine(); A beolvasás mindig string típusú változóba történik, tehát általában konvertálni kell! Példa: int x = int.parse(console.readline()); Várakozás egy billentyű lenyomására: Console.ReadKey(); A program végén kötelező! (Egyébként a konzol automatikusan bezárul a program lefutása után.) Kiírás a konzolra: Console.Write(kifejezés); Kiírás a konzolra soremeléssel: Console.WriteLine(kifejezés); A kifejezés lehet karaktersorozat ("" között), vagy változónév. Változók értékeit és szöveget együtt a helyőrzők segítségével tudjuk kiíratni. 73
74 Helyőrző: a szövegben elhelyezett 0, 1, alakú kifejezés, ahova a fordító a felsorolás sorrendjében behelyettesíti a változók értékét. Példa (x és y nevű változó értékének kiíratása): Console.WriteLine("x értéke: 0, y értéke: 1", x, y); Képernyő törlése: Console.Clear(); Betűszín megváltoztatása: Console.ForegroundColor = ConsoleColor.szín; Háttérszín megváltoztatása: Console.BackgroundColor = ConsoleColor.szín; A színt itt csak 16 színből lehet kiválasztani. AZ ALKALMAZÁS ELKÉSZÍTÉSE Oldjuk meg a következő feladatot konzolos alkalmazással! Olvassunk be tanulmányi átlagokat a billentyűzetről, és tároljuk el őket egy tömbben! Ügyeljünk arra, hogy csak 1 és 5 közötti értéket fogadjunk el, és 0 végjelig olvassunk be, de legfeljebb 50 darabot. Számoljuk ki az átlagok átlagát, számoljuk meg, hány jeles rendű tanuló van (akinek az átlaga 4,5 vagy afelett), adjuk meg a legkisebb átlagot és keressük meg, van-e kitűnő tanuló (5,0-s átlaggal)! Az eredményeket írjuk ki a képernyőre! ábra A program egy lehetséges kimenete Készítsünk új Console Application típusú projektet, és mentsük el egy külön mappába! A projekt felépítése hasonlít a Windows Forms alkalmazáséhoz, de kevesebb állományból áll. A program kódja ebben az esetben a Program.cs nevezetű fájlban található. Első lépésként vegyük fel a MAX nevű konstansba az elemszámot, és a tömböt. Amennyiben azt szeretnénk, hogy ezek az egész programból láthatóak legyenek, akkor a Main metódus elé kell írnunk a következő két sort: const int MAX = 50; static double[] atlagok = new double[max]; Mivel a Main metódus statikus metódus (azaz csak egyetlen példányban létezik, és a Program osztályhoz tartozik), ezért minden változót, metódust is statikusként kell felvennünk. Erre szolgál a deklarációban a static kulcsszó. 74
75 A program indulása után töröljük le a képernyőt, és írassuk ki a fejlécet. Windows Forms alkalmazás esetén sokkal egyszerűbben meg tudtuk az alkalmazás kezelőfelületét tervezni a címkékkel, gombokkal, itt azonban mindent kódolnunk kell. A továbbiakban a megadott kódsorokat a Main metódus törzsébe kell írnunk. Console.Clear(); Console.WriteLine("Átlagok beolvasása"); Console.WriteLine(" \n"); Az utolsó sor végén a szövegbe írt \n egy extra soremelést eredményez, ez helyettesíthető lenne egy Console.WriteLine() sorral. Vegyünk fel néhány változót! Szükségünk lesz egyre, amiben számoljuk, hány átlagot sikerült beolvasni (db), egy ciklusváltozó (i), és egy átmeneti változó (be), amibe a következő értéket fogjuk beolvasni. int db = 0; int i; double be; A következőkben egymásba kell ágyaznunk egy végjelig történő beolvasást és egy ellenőrzött adatbevitelt. Ezt két hátultesztelő ciklussal tudjuk megoldani. A külső ciklus: do // ide jön a belsı ciklus while (db<max && be!=0); A végjelig történő beolvasás addig megy, amíg a darabszám nem éri el a MAX értékét, illetve a legutoljára beolvasott érték nem a végjel. A belső ciklus: do // ide jön a belsı ciklus magja while (be!=0 && (be < 1 be > 5)); A belső ciklus feltételében figyelnünk kell arra, hogy a végjel mindenképpen megszakítsa a ciklust, ugyanakkor amennyiben 1-nél kisebb vagy 5-nél nagyobb értéket ad meg a felhasználó, a beolvasást meg kell ismételni. 75
76 A belső ciklus magja: Console.Write("Kérem a(z) 0. átlagot: ", db + 1); be = double.parse(console.readline()); if (be!= 0) if (be >= 1 && be <= 5) atlagok[db++] = be; else Console.WriteLine("A megadott érték nem 1 és 5 közötti! Kérem, adja meg újra!"); Beolvasás előtt illik egy üzenetben tájékoztatni a felhasználót, hogy melyik értéket kérjük tőle. (Azért a db+1 értéket íratjuk ki, mert a tömbelemek sorszámozása 0-tól kezdődik, de olyat nem szoktunk írni, hogy kérem a 0. elemet.) Az első elágazásra azért van szükség, mert csak akkor kell a beolvasott értékkel foglalkoznunk, ha az nem végjel. Utána pedig megvizsgáljuk, hogy a felhasználó helyes értéket adott-e meg, és ha igen, azt elhelyezzük a tömbben, egyébként pedig hibát üzenünk. Következik az eredmények kiírása, először megjelenítjük a fejlécet: Console.WriteLine("\nEredmények"); Console.WriteLine(" \n"); Az átlagszámítás a megszokott módon, az összegzés programozási tételének segítségével valósítható meg: double atl = 0; for (i = 0; i < db; i++) atl += atlagok[i]; atl /= db; A kiíratásnál gondban lehetünk, mert adott esetben nagyon csúnya törtszámok jelenhetnek meg a képernyőn. Annak érdekében, hogy ezt megakadályozzuk, formázott kiíratást kell alkalmaznunk: Console.WriteLine("Az átlagok átlaga: 0:#.##", atl); A helyőrzőbe írt #.## azt jelenti, hogy egy számot követően tizedespont, majd pedig két tizedesjegy következik. Amennyiben 1-nél kisebb törtszámot akarunk kiíratni, akkor a formázó sztring a következőképpen néz ki: #0.##. 76
77 Következik a jeles rendűek megszámlálása: int jeles = 0; for (i = 0; i < db; i++) if (atlagok[i] >= 4.5) jeles++; Console.WriteLine("Jeles rendőek száma: 0", jeles); A legkisebb átlagot minimumkiválasztással tudjuk meghatározni: double min = atlagok[0]; for (i = 1; i < db; i++) if (atlagok[i] < min) min = atlagok[i]; Console.WriteLine("A legkisebb átlag: 0", min); Végül egy lineáris keresés következik. i = -1; bool van = false; while (i < db-1 &&!l) i++; if (atlagok[i] == 5) van = true; if (van) Console.WriteLine("Van kitőnı tanuló."); else Console.WriteLine("Nincs kitőnı tanuló."); A program végén várnunk kell egy billentyű lenyomására, máskülönben a konzol ablaka automatikusan bezárul, mielőtt el tudnánk olvasni a tartalmát. Minden konzol módú alkalmazás utolsó utasítása a következő legyen: Console.ReadKey(); EGYSZERŰBB FELADATOK Bővítsd ki a programodat: a végén írasd ki az összes beolvasott átlagot egymás mellé, különböző színekkel jelölve a 4,5 és afeletti, valamint 2,0 és az alatti átlagokat! Egészítsd ki a programot kivételkezeléssel: amennyiben a beolvasás során a felhasználó nem megfelelő típusú adatot ír be, hibaüzenettel figyelmeztesd! ÖSSZETETTEBB FELADAT A mellékelt pontszamok.txt fájlban nevek és hozzájuk tartozó pontszámok vannak. Olvasd be egy rekordokat tartalmazó tömbbe őket, és írd ki a legtöbb pontot elérő tanuló nevét és pontszámát! Kérj be a felhasználótól egy pontszámot, és számold meg, hányan értek el legalább ennyi pontot! Az eredményt jelenítsd meg a képernyőn! 77
78 METÓDUSOK Ennek a témakörnek az a célja, hogy megtanuld, miképpen kell a többször, többféle adattal végrehajtandó utasításokat egy névvel ellátott, paraméterezhető egységbe, azaz metódusba szervezni. Ezek segítségével a gyakran végzett tevékenységekhez elég lesz egyszer megírni egy alprogramot, és azt később bármikor fel tudod majd használni. ELMÉLETI ÖSSZEFOGLALÓ Gyakran szükségünk van arra, hogy egy utasítássorozatot névvel ellátva külön egységbe szervezzünk, ezeket metódusoknak hívjuk. Metódusok fajtái: visszatérési értékkel rendelkező (ezt más nyelvekben függvénynek hívjuk) void visszatérési értékű, azaz visszatérési érték nélküli (ezt más nyelvekben eljárásnak hívjuk). Általános alak: minısítık típus Név (formális paraméterek) utasítások; return visszatérési_érték; Megjegyzés: a return utasítás csak akkor kell, ha a visszatérési érték típusa nem void! Minősítők: privát (osztályon kívülről nem elérhető), vagy publikus (osztályon kívülről is elérhető): private vagy public kulcsszó statikus (az osztályhoz tartozik, és egy példányban jön létre): static kulcsszó A típus a visszatérési érték típusa (bármilyen típus használható) vagy void (típus nélküli). A formális paraméterek megadják, hogy a metódus milyen típusú paraméterekkel rendelkezik, és azok milyen sorrendben követik egymást. A formális paraméterek típus név alakúak, vesszővel elválasztva. Amennyiben nincs formális paraméter, a () akkor is kell! Az aktuális paraméterek metódus hívásakor a konkrét változók vagy kifejezések, amelyeket átadunk a metódusnak. Metódusok hívása: Visszatérési érték nélküli metódus esetén: Név (aktuális paraméterek); Visszatérési értékkel rendelkező metódus esetén: változó = Név (aktuális paraméterek); Refaktorizáció: egy kijelölt utasítássorozatot a C# automatikusan metódussá alakít. 78
79 A refaktorizáció használata: 1. jelöljük ki a kívánt kódsorokat 2. kattintsunk a jobb egérgombbal 3. a megjelenő menüben válasszuk a Refactor, majd azon belül az Extract Method parancsot 4. a megjelenő ablakban adjunk nevet az új metódusnak, majd nyomjuk meg az OK gombot. Eredeti kód: static void Main(string[] args) int x = 12; int y = 24; int s = x; x = y; y = s; Refaktorizált kód: static void Main(string[] args) int x = 12; int y = 24; Csere(ref x, ref y); private static void Csere(ref int x, ref int y) int s = x; x = y; y = s; Paraméterátadási módok: Érték szerinti: a paraméter értékét lemásolja, tehát az eredeti érték nem változik meg (ez az alapértelmezett átadási mód) Cím szerinti: a változó címét adja át, tehát minden módosítás megváltoztatja az eredeti értéket is. Két fajtája van: ref és out paraméterátadás, a paraméter típusa elé kell írni a megfelelő kulcsszót (a formális és aktuális paraméterlistában is). Ref és out paraméterátadás: ref (referencia szerinti paraméterátadás): akkor használjuk, ha az átadott változó már rendelkezik értékkel, és azt a metódus meg fogja változtatni. out (kimenő paraméterátadás): akkor használjuk, ha az átadott változó még nem rendelkezik értékkel, és azt a metódustól fogja megkapni. 79
80 Példa: Csere eljárás privát statikus nincs visszatérési érték private static void Csere (ref int x, ref int y) int s = x; referencia szerint átadott paraméter x = y; y = s; A Csere eljárás meghívása: Csere(ref x, ref y); A ref szócskát híváskor is fel kell tüntetni! A PROGRAM ÁTALAKÍTÁSA METÓDUSOKKAL Alakítsuk át a feladatban elkészített programunkat! Emeljük ki a programozási tételek algoritmusait metódusokba! Bővítsük ki a programot egy minimumkiválasztásos rendezéssel, amely metódusokból épül fel, és a bemutatott Csere eljárást használja fel! Nyissuk meg a feladatban elkészített programunkat! Jelöljük ki az átlagszámítás kódsorait, majd kattintsunk a jobb egérgombbal, és a megjelenő menüben válasszuk a Refactor, majd az Extract Method menüpontot. A felbukkanó ablakba írjuk be az Atlag szót, és nyomjuk meg az OK gombot! A refaktorizáció eredménye (a kódsorok helyén): double atl; Atlag(db, out i, out atl); A C# 2008 által létrehozott metódus pedig a következőképpen fest: private static void Atlag(int db, out int i, out double atl) atl = 0; for (i = 0; i < db; i++) atl += atlagok[i]; atl /= db; Ez így azonban nem teljesen megfelelő. Egyrészt a paraméterlista számos felesleges paramétert tartalmaz, ugyanakkor a legfontosabbat, a tömböt nem tartalmazza. Másrészt, mivel az algoritmus egyetlen értéket (egy átlagot) állít elő, ezért célszerűbb lenne úgy megírni, hogy az legyen a metódus visszatérési értéke. Írjuk át a metódust! Első lépésként írjuk át a metódus fejlécét! A visszatérési érték típusa legyen double, a paraméterlistából vegyük ki az i-t és az atl-t, és egészítsük ki a tömbbel: 80
81 private static double Atlag(double[] atlagok, int db) Természetesen a metódus törzsében is sok mindent meg kell változtatnunk. Az i és az atl nevű változók lokális változók (azaz csak ott használjuk őket, és az élettartamuk is addig tart, amíg az alprogram működik). Emiatt ezeket a metódusban deklarálni kell. Másrészt, mivel van visszatérési érték, ezért ki kell egészítenünk a törzset egy return utasítással, ami visszaadja az atl változó értékét. (A kódban dőlt betűvel találhatók a változtatások.) A metódus teljes kódja a következőképpen alakul: private static double Atlag(double[] atlagok, int db) double atl = 0; for (int i = 0; i < db; i++) atl += atlagok[i]; atl /= db; return atl; Természetesen a híváson is módosítani kell. A visszatérési értéket egy változóval tesszük egyenlővé: double atl = Atlag(atlagok, db); Folytassuk a megszámlálással! Mivel az is egyetlen értéket állít elő, célszerű függvényként megírni. Hasonló lépéseket kell követni, mint az előző esetben, az eredmény: private static int Jeles(double[] atlagok, int db) int jeles = 0; for (int i = 0; i < db; i++) if (atlagok[i] >= 4.5) jeles++; return jeles; És a hívás: int jeles = Jeles(atlagok, db); A minimumkiválasztáson kicsit módosítanunk kell, hogy aztán a rendezéshez fel tudjuk használni. Ahhoz ugyanis arra van szükség, hogy ne csak az tömb elejétől, hanem tetszőleges i indextől el tudjuk indítani a keresést. Továbbá, nem a minimum értékére, sokkal inkább a helyére (indexére) lesz szükségünk, és ezt fogjuk visszaadni egy out paraméterben. 81
82 private static void MinKiv(double[] atlagok, int db, int i, out int ind) ind = i; for (int j = i+1; j < db; j++) if (atlagok[j] < atlagok[ind]) ind = j; Az eljárásnak érték szerint átadunk egy i paramétert, ami a minimumkiválasztás kezdetét jelzi. Éppen ezért a ciklusváltozót át kell j-re neveznünk. A ciklus így nem 1-től, hanem i+1-től indul. Mivel nem a minimum értékét, hanem csak a helyét jegyezzük meg, ezért módosítani kellett az elágazás feltételén és az értékadáson is. Most viszont már van egy olyan minimumkiválasztásunk, amit több célra is fel tudunk használni. Mivel alaposan megváltozott a kód, ezért a metódushívást is át kell írni. int minind; MinKiv(atlagok, db, 0, out minind); Console.WriteLine("A legkisebb átlag: 0", atlagok[minind]); Először fel kell vennünk egy változót a visszaadandó indexnek. Értéket nem szabad adni neki! Majd ezek után átadjuk a metódusnak, out paraméterként, azaz azt várjuk, hogy majd ő meghatározza az értékét. (A harmadik paraméter 0 lesz, mert itt a minimum kiválasztását a tömb legelejéről kell kezdenünk.) Természetesen a kiírást is módosítjuk, hiszen abban egy tömbhivatkozásnak kell szerepelnie. Végül a keresést kell átírni. A keresésnek két értéket kell visszaadnia: a logikai változót és a megtalált elem indexét. Mindkettőt célszerű out paraméternek deklarálni. private static void LinKer(double[] atlagok, int db, out int i, out bool van) i = -1; van = false; while (i < db-1 &&!l) i++; if (atlagok[i] == 5) van = true; A metódushívás már nem rejt nagy meglepetéseket. bool van; LinKer(atlagok, db, out i, out van); Most már csak a rendezés van hátra. Ehhez először írjuk a már elkészült metódusok után a fejezet elején levő Csere eljárást, azzal a különbséggel, hogy az int típust cseréljük doublera! 82
83 A rendezés alapelve, hogy egy ciklussal végigmegyünk a tömb elejétől az utolsó előtti elemig. A ciklusváltozó értéke kijelöli azt az indexet, amelytől kezdve lefuttatunk egy minimumkiválasztást, majd a minimumként talált elemet megcseréljük a ciklusváltozó által kijelölt elemmel. Így mindig az aktuális szakasz legkisebb eleme kerül a tömb elejére, tehát a végén a tömb teljesen rendezett lesz. Mivel a rendezés módosítja a tömb elemeit, ezért cím szerint adjuk át, és mivel már fel van töltve értékekkel, ezért azt ref paraméternek kell felvennünk. private static void Rendez(ref double[] atlagok, int db) for (int i = 0; i < db - 1; i++) int ind; MinKiv(atlagok, db, i, out ind); Csere(ref atlagok[i], ref atlagok[ind]); Itt tudjuk kihasználni a speciális minimumkiválasztást, hiszen a jól paraméterezett metódusnak köszönhetően meg tudjuk azt csinálni, hogy csak a paraméterként átadott i. elemtől kezdve indítjuk el a kiválasztást. Az utolsó lépés, hogy a főprogram végén meghívjuk a rendezést, majd a rendezett tömböt kiíratjuk a képernyőre: Rendez(ref atlagok, db); Console.WriteLine("\nÁtlagok"); Console.WriteLine(" \n"); for (i = 0; i < db; i++) Console.Write("0 ", atlagok[i]); EGYSZERŰBB FELADATOK Készíts egy új konzol alkalmazást! Írj metódust, amely a paraméterként megadott szám faktoriálisával tér vissza! (Emlékeztető: n! = 1 2 n). Írj hozzá főprogramot, amely bekér egy számot, kiszámolja a faktoriálist, és kiírja az eredményt! Készíts egy új konzol alkalmazást! Írj metódust, amely a két paraméterben megadott szám legnagyobb közös osztóját számítja az euklideszi algoritmus segítségével! Írj hozzá főprogramot, amely bekéri a két számot, kiszámolja az LNKO-t, és kiírja azt! Euklideszi algoritmus: Függvény Euklidesz (a,b: Egész): Egész m := a MOD b Ciklus amíg m 0 a := b b := m m := a MOD b Ciklus vége Visszaad: b Függvény vége. Megjegyzés: 1. a MOD művelet az osztás maradékát jelenti. (C#-ban: %) 2. az algoritmus feltételezi, hogy a>=b! 83
84 ÖSSZETETTEBB FELADAT Készítsd el a és a pontban megírt metódusok rekurzív változatát! Próbáld is ki őket a főprogramból! Rekurzív algoritmusnak nevezzük az olyan algoritmust, amely valamely pontján önmagára hivatkozik. Ezeket általában rekurzív (önmagát meghívó) függvényként szoktuk megvalósítani a programozási nyelvek segítségével. Segítségképpen megadjuk a faktoriális és az LNKO rekurzív definícióját, amely gyakorlatilag egy az egyben átírható rekurzív függvénnyé: 1 =1 = 1 = = 84
85 MÁTRIXOK Ennek a témakörnek az a célja, hogy elsajátítsd a kétdimenziós tömbök mátrixok kezelését. Megtanulhatod, hogyan kell feltölteni, megjeleníteni őket, valamint számos alapvető algoritmust megírni rájuk. A való életben nagyon sok helyen kell táblázatokkal dolgozni, amelyek tulajdonképpen mátrixok, így nagyon fontos, hogy tudjunk olyan programokat írni, amelyek ilyen adatszerkezetekkel dolgoznak. ELMÉLETI ÖSSZEFOGLALÓ A kétdimenziós tömböt mátrixnak nevezzük. Mátrix típusú változó deklarációja: bázistípus[,] változónév; Vegyük észre, hogy ez mindössze egyetlen vesszőben tér el a vektortól Helyfoglalásnál meg kell adni, hogy legfeljebb hány sorból (MAXN), illetve hány oszlopból (MAXM) állhat a mátrix. Példa: int[,] matr = new int[maxn,maxm]; Hivatkozás a tömb egy elemére: matr[i,j], ahol: 0 i<n és 0 j<m. A mátrix bejárása csak dupla ciklussal lehetséges! Feltöltés soronként, szövegfájlból: A szövegfájl soraiban számok, szóközzel elválasztva Probléma: egyszerre egy sort tudunk beolvasni A beolvasott sort a szóközök mentén szét kell tördelni Egy sor széttördelése a szóközök mentén: string[] darabolt = sor.split(' '); Az eredmény egy stringekből álló tömb, amit még elemenként át kell konvertálni! 85
86 Feltöltés szövegfájlból, soronként: StreamReader f = File.OpenText("forras.txt"); for (int i = 0; i < n; i++) string sor = f.readline(); string[] darabolt = sor.split(' '); for (int j = 0; j < m; j++) matr[i, j] = int.parse(darabolt[j]); Programozási tételek mátrixokra Összegzés tétele: adott a matr[0..n-1,0..m-1] mátrix, amelynek elemein értelmezhető az összeadás művelete. Számítsuk ki a mátrix elemeinek összegét (ossz változó)! Algoritmus: ossz := 0 ciklus i:=0-tól n-1-ig ciklus j:=0-tól m-1-ig ossz := ossz + matr[i,j] ciklus vége. ciklus vége. Átlagszámításnál n*m-mel kell osztani! Megszámlálás tétele: adott a matr[0..n-1,0..m-1] mátrix, amelynek elemein értelmezhető az adott tulajdonságfüggvény. Számoljuk meg, hogy a mátrixban hány darab, az adott tulajdonságnak megfelelő elem található (db változó)! Algoritmus: db := 0 ciklus i:=0-tól n-1-ig ciklus j:=0-tól m-1-ig ha matr[i,j] adott tulajdonságú akkor db := db + 1 ciklus vége. ciklus vége. 86
87 Minimum- és maximumkiválasztás tétele: adott a matr[0..n-1,0..m-1] mátrix, amelynek elemein értelmezhető a kisebb / nagyobb reláció. Válasszuk ki a mátrix legkisebb / legnagyobb elemét! A mini és a minj változók jelentsék a szélsőérték helyét, azaz sor- és oszlopindexét! Algoritmus: mini := 0 minj := 0 ciklus i:=0-tól n-1-ig ciklus j:=0-tól m-1-ig ha matr[i,j]<matr[mini,minj] akkor mini := i minj := j ciklus vége. ciklus vége. Lineáris keresés tétele: adott a matr[0..n-1,0..m-1] mátrix, amelynek elemein értelmezhető az adott tulajdonságfüggvény. Keressük meg (sorfolytonosan) a mátrix első adott tulajdonságú elemét! Az l logikai változó legyen igaz, ha sikerült találni ilyen elemet, és ekkor az i és j tartalmazza az első adott tulajdonságú elem sor- és oszlopindexét! Algoritmus: l := hamis i := -1 ciklus amíg i<n-1 és (NEM l) i := i+1 j := -1 ciklus amíg j<m-1 és (NEM l) j := j+1 ha matr[i,j] adott tulajdonságú akkor l:=igaz ciklus vége. ciklus vége. Mátrixok grafikus megjelenítése: (például DataGridView komponens segítségével) Sorok számának beállítása: datagridview1.rowcount = n; Oszlopok számának beállítása: datagridview1.columncount = m; Cellák feltöltése adattal: datagridview1.rows[i].cells[j].value = matr[i, j]; MÁTRIXOS FELADAT Készítsük egy új Windows Forms alkalmazást! Egy szövegfájlban (homerseklet.txt) soronként, szóközzel elválasztva hőmérséklet-mérési adatok vannak. Olvassuk be ezeket egy mátrixba, és válaszoljunk az alábbi kérdésekre! A válaszokat írjuk a képernyőre! a) Mennyi volt az átlaghőmérséklet? b) Hányszor mértek 10 fok alatti hőmérsékletet? c) Mennyi volt a legmelegebb mért érték? d) Kérjünk be a felhasználótól egy értéket, és állapítsuk meg, hogy volt-e ilyen mérés, és ha igen, melyik nap és hányadik alkalommal! 87
88 Készítsünk egy teljesen új Windows Forms alkalmazást. Tervezzük meg a felületét az alábbi ábrának megfelelően! ábra A hőmérséklet-mérések adatait feldolgozó program futásának eredménye Helyezzük el a gombokat, címkéket és szövegmezőket, illetve helyezzünk el a Formra egy DataGridView komponenst! Állítsuk be a tulajdonságait az alábbiaknak megfelelően! DataGridView tulajdonságai: o Mivel alapból meglehetősen hosszú nevet kap a Formra helyezett objektumpéldány, és ez nagyon hosszú hivatkozást eredményez a kódban, érdemes azt lerövidíteni. A (Name) tulajdonságot állítsuk dgv-re, így a továbbiakban ezzel a névvel tudunk a rácsra hivatkozni. o Ez a komponens tulajdonképpen adattáblák megjelenítéséhez készült, ezért alkalmas több sor kijelölésére, a cellákban levő értékek szerkesztésére, illetve sorok törlésére is. Ezekre a funkciókra azonban nekünk nincsen szükségünk, mert most csak megjelenítésre használjuk, úgyhogy ezeket a lehetőségeket tiltsuk le. AllowUserToDeleteRows: false (a felhasználó ne tudjon sorokat törölni) AllowUserToAddRows: false (a felhasználó ne tudjon kézzel sorokat hozzáadni) MultiSelect: false (egyszerre ne lehessen több cellát kijelölni) ReadOnly: true (mivel csak megjelenítésre használjuk, ezért csak olvashatóra kell állítanunk). A rács többi tulajdonságát majd az adatokkal történő feltöltéskor, kódból fogjuk beállítani. Kezdjük el megírni a programot! A továbbiakban mindent metódusok segítségével fogunk megvalósítani. A program elején deklaráljuk a mátrixot, és azt a két változót, ami a sorok és oszlopok számát fogja tartalmazni: int[,] matr; int n, m; Amennyiben belenézünk a mellékelt szövegfájlba, az első sorban szóközzel elválasztva szerepel, hogy a mátrixnak hány sora és oszlopa van, tehát először ezt kell beolvasni, és elhelyezni a megfelelő változókban. Ezek után ráérünk lefoglalni egy pontosan akkora mátrixot, és így memóriát is spórolunk. 88
89 Kezdjük el megírni a feltöltést végző metódust! Paraméterként adjuk át a feltöltendő mátrixot! Mivel nem biztos, hogy fel tudjuk tölteni (például hiányozhat a szövegfájl), ezért ref paraméterként fogjuk átadni. private void Feltolt(ref int[,] matr) if (File.Exists("homerseklet.txt")) StreamReader f = File.OpenText("homerseklet.txt"); string sor = f.readline(); string[] darabolt = sor.split(' '); n = int.parse(darabolt[0]); m = int.parse(darabolt[1]); matr = new int[n, m]; Először ellenőrizzük, hogy a szövegfájl rendelkezésre áll-e. Majd megnyitjuk olvasásra, és beolvassuk az első sorát. A fejezet elején az Elméleti összefoglalóban is olvasható, hogy a Split függvény képes stringeket egy megadott karakter mentén szétdarabolni. Mivel az első sorban csak egy szóköz van, ezért a keletkezett darabolt tömb mindössze két elemet fog tartalmazni, ezeket egésszé alakítjuk, és értékül adjuk n és m változóknak. Amikor ezek az értékek megvannak, akkor helyet foglalunk a mátrixnak. A mátrix elemeinek feltöltése az Elméleti összefoglalóban is látható módon mehet végbe. Feltételezhetjük, hogy a megadott szövegfájl helyes, azaz egy annyi sor és egy sorban annyi elem van, amennyit a szövegfájl első sorában elhelyezett értékek mondanak. else for (int i = 0; i < n; i++) sor = f.readline(); darabolt = sor.split(' '); for (int j = 0; j < m; j++) matr[i, j] = int.parse(darabolt[j]); f.close(); MessageBox.Show("A fájl nem található."); A következő lépés a mátrix megjelenítése a DataGridView segítségével. A megjelenítést végző metódusban először a komponens tulajdonságait állítjuk be az adatoknak megfelelően: private void Megjelenit(int[,] matr, int n, int m) dgv.rowcount = n; dgv.columncount = m; int i, j; A RowCount tulajdonsággal a sorok, míg a ColumnCount segítségével az oszlopok száma állítható be. 89
90 for (i = 0; i < n; i++) dgv.rows[i].headercell.value = (i + 1).ToString() + ". nap"; dgv.rowheaderswidth = 80; A sorok előtt feltüntetjük a napok sorszámát a fenti ciklus segítségével. Azért az i+1 értékét kell kiírnunk, mert a számozás nullától kezdődik. Mivel az így beírt felirat nem férne ki alapból a cellába, ezért a sorfejléc szélességét a RowHeadersWidth tulajdonsággal meg kell növelni. for (j = 0; j < m; j++) dgv.columns[j].headertext = (j + 1).ToString() + ". mérés"; dgv.columns[j].width = 48; A második ciklus az oszlopfeliratokat és az oszlopok szélességét állítja be. Célszerű viszonylag keskeny oszlopokat csinálni, mert a cellákban levő érték viszonylag keskeny, az oszlopfeliratot pedig úgyis automatikusan tördeli a rendszer. Ennyi előkészítés után most már csak végig kell mennünk a mátrix elemein, és el kell őket helyezni a megfelelő cellákban: for (i = 0; i < n; i++) for (j = 0; j < m; j++) dgv.rows[i].cells[j].value = matr[i, j]; A gombra kattintáskor már csak meg kell hívni az általunk megírt metódusokat: Feltolt(ref matr); Megjelenit(matr, n, m); Ugyanitt kell engedélyezni a többi nyomógombot is, ha a mintaprogramnak megfelelően írjuk meg a saját programunkat. 90
91 Valósítsuk meg a programozási tételeket az Elméleti összefoglalóban található algoritmusok alapján! Az első legyen az összegzés: private int Osszeg(int[,] matr, int n, int m) int ossz = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) ossz += matr[i, j]; return ossz; Az összegzés felhasználásával írjuk meg az átlagszámítást! Nincs más teendőnk, csak a kiszámolt összeget el kell osztanunk a mátrix elemszámával. private double Atlag(int[,] matr, int n, int m) return (double)osszeg(matr, n, m) / (n * m); Mivel az Osszeg metódus egész értéket ad vissza, és az n*m értéke is egész szám, ezért mindenképpen típuskényszerítést kell alkalmaznunk, hogy az átlagszámítás eredménye valós szám legyen. Erre szolgál a függvényhívás elé írt (double) szócska. A gombra kattintáskor meghívjuk az Atlag függvényt, és a visszaadott értéket kiírjuk egy címkére. A ToString() metódus meghívásakor két tizedesre kerekítést alkalmazunk: double atlag = Atlag(matr, n, m); label1.text = atlag.tostring("#.##") + " fok"; Következik a megszámlálás, amelynek feltétele, hogy a mátrix eleme 10-nél kisebb (azaz 10 foknál alacsonyabb hőmérsékletet mértek): private int Szamol(int[,] matr, int n, int m) int db = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (matr[i, j] < 10) db++; return db; És a meghívása: int db = Szamol(matr, n, m); label2.text = db.tostring() + " alkalommal"; 91
92 A legmagasabb hőmérsékletet maximumkiválasztással tudjuk meghatározni. Paraméterként visszaadjuk a legnagyobb elem két indexét: private void Maximum(int[,] matr, int n, int m, out int maxi, out int maxj) maxi = 0; maxj = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (matr[i, j] > matr[maxi,maxj]) maxi = i; maxj = j; A meghívásnál arra kell figyelnünk, hogy csak az indexeket kapjuk vissza, tehát kiíratásnál a mátrix adott indexű elemét kell megjelenítenünk: int maxi, maxj; Maximum(matr, n, m, out maxi, out maxj); label3.text = matr[maxi, maxj].tostring() + " fok"; Amennyiben vizuálisan is szeretnénk jelölni a legnagyobb elemet, megtehetjük, hogy az adott indexű cellát kijelöljük, a következőképpen: dgv.rows[maxi].cells[maxj].selected = true; A keresés van hátra. Paraméterként adjuk át az adott nevű változóban a felhasználó által beírt hőmérséklet értéket. Az Elméleti összefoglalóban megadott algoritmushoz képest még egy dolgon kell változtatni: private void Keres(int[,] matr, int n, int m, int adott, out bool l, out int i, out int j) l = false; i = -1; j = -1; while (i < n - 1 &&!l) i++; j = -1; while (j < m - 1 &&!l) j++; if (matr[i, j] == adott) l = true; 92
93 A bekeretezett utasításra azért van szükség, mert a logikai változót és az indexeket kimenő (out) paraméterként vettük fel. Mivel a j változó eredetileg csak a ciklusban kapna értéket, amely viszont nem biztos, hogy lefut, ezért a rendszer nem engedi lefordítani a programot, csak akkor, ha egy plusz értékadással biztosítjuk, hogy az összes kimenő változó biztosan kap értéket a metódus során. A metódushívás előtt a felhasználó által a szövegmezőbe beírt értéket át kell konvertálnunk, és azért, hogy ez hibamentesen végbemenjen, kivételkezelést fogunk használni. A megkapott értékkel meghívjuk a Keres eljárást, és a visszaadott értékeknek megfelelően írjuk ki az eredményt. Amennyiben megtaláltuk a keresett elemet, azt is megjelenítjük, hogy melyik nap, melyik mérésnél bukkantunk rá. Sőt, a maximumnál alkalmazott cellakijelöléssel is jelezhetjük ezt: try int adott = int.parse(textbox1.text); bool l; int i, j; Keres(matr, n, m, adott, out l, out i, out j); if (l) label5.text = "Van ilyen, " + (i + 1).ToString() + ". nap, " + (j + 1).ToString() + ". mérés."; dgv.rows[i].cells[j].selected = true; else label5.text = "Nem volt ilyen mérés."; catch (FormatException) label5.text = "Hibás formátumú adat."; Ezzel végére értünk a feladat megoldásának. EGYSZERŰBB FELADAT Egészítsd ki a pontban elkészített programot! Számold ki az átlaghőmérsékleteket naponként (soronként) és mérésenként (oszloponként)! Az eredményeket jelenítsd meg a rácsban is! ÖSSZETETTEBB FELADAT Véletlen számokkal tölts fel egy négyzetes mátrixot, úgy, hogy csak alsó vagy felső háromszög mátrix legyen (csak a főátlóban és alatta, illetve a másik esetben felette legyenek 0-tól különböző értékek)! Jelenítsd meg a mátrixot a rács segítségével! Az előző fejezetben használt Csere eljárás felhasználásával transzponáld a mátrixot (tükrözd a főátlójára)! Az eredményt minden esetben kövesd a rácsban is! 93
94 OSZTÁLYOK Ennek a témakörnek az a célja, hogy saját osztályt tudj készíteni. Amikor egy olyan egységet kell készíteni, amelynek jól körülhatárolható jellemzői és feladatai vannak, akkor nagyon jól jön, ha osztályt és abból példányosítva objektumokat tudunk létrehozni. Ebben és a későbbi fejezetekben saját típusokat készítünk, amelyek segítségével különféle adatszerkezeteket valósíthatunk meg. ELMÉLETI ÖSSZEFOGLALÓ Osztály: azonos tulajdonságokkal (jellemzőkkel) és viselkedéssel rendelkező egyedeket zárja egységbe. A tulajdonságok értékét adatok tárolják. A viselkedéseket pedig metódusok írják le. Objektum: az osztály egy példánya, egyede (osztály típusú változó). A t k (tu á k t ke ) Metó u k (v e ke ek) O tá y Egy osztály felépítése: class Név // konstruktor(ok) // metódus(ok) // adattag(ok) Az osztály minden, előbb felsorolt összetevőjéhez hozzá kell rendelnünk egy hozzáférési szintet. Hozzáférési szintek: private: az adott metódus vagy adattag csak és kizárólag az osztály valamelyik metódusából érhető el, az osztályból példányosított objektumból nem (ez az alapértelmezett, ha a kulcsszót elhagyjuk). public: az adott metódus kívülről, egy objektumból is szabadon meghívható, illetve az adott adattag kiolvasható és az értéke módosítható. Konstruktor: speciális metódus, amelynek semmilyen visszatérési értéke nincsen (még void sem!), a neve pontosan megegyezik az osztály nevével, és akkor fut le, amikor az osztályból objektumot példányosítunk (például itt adhatunk kezdőértékeket az adattagoknak). Két fajtája van: paraméter nélküli (default) konstruktor: az osztállyal együtt létrejön, de írhatunk újat paraméteres konstruktor: tetszőleges számút készíthetünk, de ezek paraméterlistájának eltérőnek kell lennie! 94
95 Példa: osztály class Proba default konstruktor public Proba() a = 0; public Proba(int x) paraméteres konstruktor a = x; public string Kiir() return a.tostring(); private int a; Objektum létrehozása: Default konstruktorral: Proba p1 = new Proba(); Paraméteres konstruktorral: Proba p2 = new Proba(100); Publikus metódus meghívása: Console.WriteLine(p2.Kiir()); A privát adattagot nem érhetjük el kívülről: p1.a = 10; NEM LEHETSÉGES! A privát adattagokat kívülről tehát nem tudjuk kiolvasni és módosítani. Ez az objektumorientáltság egyik alapelve: a megvalósítást elrejtjük a felhasználó elől. Valahogyan azonban szeretnénk a mezőket használni, és az adattagok módosítását ellenőrzötten szeretnénk megoldani (például csak bizonyos értékhatárok közötti értéket engedünk meg). A megoldás: tulajdonságok használata. Tulajdonságok deklarációja: minısítı típus tulajdonságnév get // kiolvasáskor lefutó kód set // értékadáskor lefutó kód publikus metódus privát adattag (mező) Példa: tulajdonság (általában nagy kezdőbetűvel) public int A get érték kiolvasásakor lefutó kód return a; set if (value <= 100) a = value; értékadáskor megadott új érték ellenőrzött értékadás 95
96 A tulajdonság típusa általában megegyezik az adattag típusával (nem kötelező). Tulajdonság használata: p1.a = 10; Console.WriteLine(p1.A); Csak olvasható tulajdonság: nincsen set rész. Csak írható tulajdonság: nincsen get rész. HALMAZ OSZTÁLY LÉTREHOZÁSA Készítsük egy Halmaz osztályt! Egyelőre tudjon egész számokat tárolni, és minden érték csak egyszer szerepelhessen a halmazban! Valósítsuk meg a következő műveleteket: elemszám lekérdezése, adott elem benne van-e a halmazban, elem hozzáadása a halmazhoz, elem kivétele a halmazból. Írjunk egy kis programot, amellyel tesztelni tudjuk a halmazunkat! Készítsünk egy Windows Forms alkalmazást. Tervezzük meg a felületét az alábbi ábrának megfelelően! ábra A halmaz használatát tesztelő mintaprogram A Halmaz osztályunk megvalósítását külön állományba fogjuk helyezni, annak érdekében, hogy máskor is fel tudjuk használni (hozzá tudjuk adni egy másik projekthez is). Kattintsunk jobb egérgombbal a projekt nevére (vastag betűs sor a Solution Explorerben), a megjelenő menüben válasszuk az Add / Class menüpontot. A felbukkanó ablakba írjuk be, hogy Halmaz, majd nyomjuk meg az Add gombot. Ekkor létrejön az üres halmaz osztály definíciója egy Halmaz.cs állományban, amelyet a fejlesztői környezet automatikusan hozzáad a projekthez. A halmazt egyelőre egy rendezetlen tömbben (vektorban) fogjuk megvalósítani. Szükségünk van egy aktuális, és egy maximális elemszámra (ez utóbbi lehet konstans), ezeket privát mezőként fogjuk felvenni. 96
97 Halmaz osztályunk tehát jelenleg így fest. class Halmaz private int n; private const int MAX = 200; private int[] elemek; Először írjuk meg a konstruktort. Ebben nullázzuk ki az n értékét, és inicializáljuk a tömböt: public Halmaz() n = 0; elemek = new int[max]; Az elemszám privát mező, de olykor szükség lehet rá, hogy lekérdezzük az értékét. Ezt egy csak olvasható tulajdonsággal tehetjük meg: public int Elemszam get return n; A halmaz típus legfontosabb tulajdonsága, hogy egy elem csak egyszer szerepelhet a halmazban. Ennek ellenőrzéséhez is szükség lesz egy olyan, logikai értéket visszaadó függvényre, amely egy adott számról eldönti, hogy benne van-e a halmazban, vagy nincsen. Természetesen lineáris keresést használhatunk erre a célra: public bool ElemeE(int elem) int i = -1; bool l = false; while (i < n-1 &&!l) i++; if (elemek[i]==elem) l = true; return l; Amennyiben a ciklus leáll, mielőtt az i eléri az n értékét, az azt jelenti, hogy megtaláltuk a tömbben a paraméterként átadott elemet. Ekkor igaz értéket kell visszaadni, hiszen a megadott érték eleme a halmaznak. Ennek felhasználásával már könnyen meg tudjuk írni azt a metódust, amely elemet helyez el a halmazban. Az új elemet a tömb végére fogjuk betenni, feltéve, hogy az még nincs benne (ezt ellenőrizni tudjuk az előbb megírt függvénnyel), illetve hogy nem értük még el a vektor maximális elemszámát. public void Halmazba(int elem) if (!ElemeE(elem) && n < MAX) elemek[n++] = elem; 97
98 Az elem eltávolítása a halmazból már felvet néhány kérdést. Természetesen csak akkor tudunk valamit kivenni a tömbből, ha az benne van. Viszont honnan fogjuk tudni, hogy a vektor hányadik elemét kell törölni? És egyáltalán hogyan tudunk elemet kitörölni a tömbből? Ahhoz hogy tudjuk, hányadik elemet kell eltávolítani, vegyünk fel egy új privát mezőt a többi közé, amelybe a kereséskor elmentjük a megtalált elem indexét. class Halmaz private int n; private int talalt; private const int MAX = 200; Módosítanunk kell a keresést is: public bool ElemeE(int elem) int i = -1; bool l = false; while (i < n-1 &&!l) i++; if (elemek[i]==elem) l = true; talalt = i; return l; Ennek segítségével meg tudjuk írni a halmazból kivételt is. Amennyiben megtaláljuk a keresett elemet, a tömb utolsó elemét tesszük be helyére, majd csökkentjük eggyel az n értékét. Így az utolsó elemet logikailag levágjuk a tömbről. public void Halmazbol(int elem) if (ElemeE(elem)) elemek[talalt] = elemek[n - 1]; n--; Még a halmaz elemeinek kiírása van hátra. Kapcsos zárójelek között, pontosvesszőkkel elválasztva fogjuk ezt megtenni, ügyelve arra, hogy az utolsó elem után már ne tegyünk pontosvesszőt. public string Kiir() string s = " "; for (int i = 0; i < n; i++) s += elemek[i].tostring(); s += (i < n - 1? "; " : ""); s += " "; return s; 98
99 Először az összeállítandó stringben elhelyezünk egy nyitó kapcsos zárójelet. A ciklus végigmegy a halmaz elemein, és hozzáfűzi őket s-hez. Ami érdekes, az a következő sorban levő feltételes operátor. Ez megvizsgálja a ciklusváltozó értékét, és ha még nem értük el az utolsó elemet, akkor pontosvesszőt fűz hozzá a stringhez, egyébként pedig semmit. Így az utolsó elem után már csak az a záró kapcsos zárójel lesz, amit a ciklus után fűzünk a szöveghez, mielőtt visszaadnánk. A feltételes operátor használata: feltétel? érték_ha_igaz : érték_ha_hamis Természetesen az értékek helyett utasítások is szerepelhetnek. Egyelőre elkészültünk a Halmaz osztályunkkal, a későbbi fejezetekben tovább fogjuk fejleszteni. Írjuk meg a mintaprogramot, amelyik felhasználja az újonnan készített típusunkat! Deklaráljuk a halmazt globális változóként! Halmaz h; A program indulásakor létrehozzuk a halmazt, és a listában megjelenítjük az alapállapotát, amely ilyenkor még csak egy üres halmaz. private void Form1_Load(object sender, EventArgs e) h = new Halmaz(); listbox1.items.add(h.kiir()); Az elem elhelyezése és elem kivétele metódusok egyetlen utasításban térnek el egymástól, ezért csak az egyik kódját adjuk meg. Használjunk itt is kivételkezelést, az elhelyezett szövegdobozba írt szöveg átkonvertálásának biztosításához. Mivel a halmazunkat úgy írtuk meg, hogy sem az nem okoz hibát, ha már meglévő elemet tennénk újra bele, sem az, ha benne nem lévő elemet próbálunk kivenni, ezért más hibakezelésre nincsen szükség. Minden művelet után írjuk ki újra a halmaz aktuális elemeit a listába! Következzék tehát az elem elhelyezésekor lefutó kódrészlet! try int elem = int.parse(textbox1.text); textbox1.text = ""; h.halmazba(elem); listbox1.items.add(h.kiir()); catch (FormatException) MessageBox.Show("Helytelen vagy hiányzó adat!"); A másik metódus törzsébe másoljuk át ugyanezt a kódot, csak a bekeretezett utasítást cseréljük ki erre: h.halmazbol(elem); A harmadik gomb az elemszámot kérdezi le, és írja be a listába. Ezt nagyon egyszerűen megtehetjük, hiszen készítettünk egy csak olvasható tulajdonságot, ami erre épp alkalmas: listbox1.items.add("elemszám: " + h.elemszam); Ezzel végére értünk a mintaprogram megírásának. 99
100 EGYSZERŰBB FELADAT Készíts egy új programot, amely a Halmaz osztály felhasználásával szimulál ötös- és hatoslottó-sorsolást! A programban lehessen választani a két sorsolás között, és a halmazzal biztosítsuk, hogy pontosan öt, illetve hat különböző számot húzzunk ki! ábra A lottósorsolást végző program egy lehetséges megjelenése Amennyiben már meglévő osztályt szeretnénk a projektünkhöz adni, a következő lépéseket kell végrehajtanunk: 1) Másoljuk be a Halmaz.cs fájlt az új projekt mappájába. 2) Kattintsunk jobb gombbal a projektre, és a megjelenő menüből válasszuk az Add / Existing Item menüpontot. A felbukkanó tallózó ablakban válasszuk ki a Halmaz.cs fájlt, majd kattintsunk az Add gombra. 3) Így még nem tudjuk a programunkban használni az új osztályt, mert nem egyforma a névtere (namespace) az alkalmazásunkéval. Ennek egyik lehetséges megoldása, hogy kimásoljuk a Form1.cs állományból a namespace kulcsszó után álló nevet, és a Halmaz.cs állományban írjuk felül az ott lévő régi névteret. ÖSSZETETTEBB FELADAT Készíts egy új osztályt törtek kezelésére! Tárold külön egész számként a tört számlálóját és nevezőjét! Az új osztály segítségével: a) Lehessen létrehozni paraméter nélküli konstruktorral olyan törtet, amelynek értéke 0 (a számlálója 0, nevezője pedig 1). b) Legyen egy privát eljárás, amely a 12. fejezetben ismertetett Euklideszi algoritmust valósítja meg! Egészítsük ki azzal, hogy ha a második paraméter nagyobb, mint az első, akkor gondoskodjon az értékek cseréjéről, az algoritmus futásának biztosítása érdekében! c) Legyen egy másik konstruktor is, amely a két paraméterének (számláló és nevező) megfelelő törtet hozza létre, méghozzá úgy, hogy az euklideszi algoritmus segítségével egyszerűsíti a törtet (a számlálót és a nevezőt is leosztja a legnagyobb közös osztóval)! d) Legyen egy olyan eljárás, ami kiírja a törtet számláló / nevező alakban, és egy olyan is, ami kiírja az értékét tizedes törtben is! Készíts egy kis programot, amellyel az új osztályt kipróbálhatod! 100
101 GENERIKUS OSZTÁLYOK Ennek a témakörnek az a célja, hogy olyan osztályt tudj készíteni, amely rendelkezik egy típusparaméterrel, tehát (elvileg) tetszőleges típussal példányosítható. Ez azért fontos, mert amikor egy univerzális adatszerkezetet készítünk, akkor szeretnénk azt egyszer megírni, aztán pedig minél több féle adaton használni. Képzeljük el például, hogy az általunk írt Tort osztály objektumait is be tudjuk tenni egy általunk készített Halmaz objektumba ELMÉLETI ÖSSZEFOGLALÓ Az előző fejezetben készített Halmaz osztály egyik problémája, hogy csak egész számokat tudunk a halmazba tenni, pedig elvileg bármilyen típusú adatot tárolhatnánk a mögötte lévő tömbben. A megoldás a generikus (azaz típussal paraméterezhető) osztály használata. Az osztály definiálásakor megadunk egy T típust, és mindenhol, ahol eddig int-et használtunk, most ezt a T típust fogjuk. Lehet egy osztálynak több típusparamétere is. Eredeti Halmaz osztály: class Halmaz private int[] elemek; Használat: Halmaz h = new Halmaz(); Generikus Halmaz osztály: class Halmaz<T> típusparaméter private T[] elemek; típus alkalmazása Használat: Halmaz<int> h = new Halmaz<int>(); konkrét típussal példányosítunk Vajon a T típusparaméter teljesen tetszőleges típus lehet? Nem feltétlenül Például: készítsünk olyan halmaz típust, amelyik rendezetten tárolja az elemeket! Ehhez a példányosításkor megadott típusnak ismernie kell az összehasonlítás műveletét! Azért, hogy ezt biztosítani tudjuk, megszorítást kell alkalmazni a típusparaméterre. Megszorítás a típusparaméterre: class Halmaz<T> where T:IComparable<T> Ez azt jelenti, hogy a T típusnak ismernie kell az összehasonlítás műveletét. Innentől azonban a kódban nem használhatjuk a <, >, = jeleket, hanem a CompareTo() metódust kell igénybe vennünk az összehasonlításhoz! 101
102 a és b összehasonlítása a CompareTo() függvény visszaadott értékének vizsgálatával: a és b tetszőleges típusú objektumok lehetnek, ha megvalósítják az IComparable interfészt, azaz rendelkeznek ilyen metódussal. Az összehasonlítás kódja: int x = a.compareto(b); if (x < 0) // ekkor a kisebb mint b else if (x > 0) // ekkor a nagyobb mint b else // ekkor a és b egyenlı Amennyiben a tömbben rendezetten tároljuk az elemeket, szükség van egy hatékonyabb keresésre. A logaritmikus keresés elve az, hogy felezi a sorozatot, összehasonlítja a középső elemet a keresettel, és eldönti a rendezettséget kihasználva, hogy melyik irányban érdemes tovább folytatni a keresést, és csak az így kapott rövidebb részsorozattal foglalkozik a továbbiakban. Adott a v[0..n-1] vektor, amelynek elemei valamilyen szempont szerint rendezettek. Keressük meg a vektor egy olyan elemét, amely egy adott értékkel egyenlő! Az l logikai változó legyen igaz, ha sikerült találni ilyen elemet, és ekkor az i tartalmazza az egyik ilyen elem indexét. Algoritmus: l := hamis e := 0 u := n-1 ciklus amíg (nem l) és (e <= u) i := (e+u) / 2 ha v[ind] < adott akkor e := ind + 1 különben ha v[ind] > adott akkor u := ind 1 különben l := igaz ciklus vége. Az új halmazhoz arra is szükségünk lesz, hogy a tömböt rendezzük minden beszúrás és törlés után (nem túl hatékony, de most így az egyszerűbb ). A korábban megírt minimumkiválasztásos rendezést át kell írnunk: mivel egy osztály metódusa lesz, ezért a paraméterek közé nem kell felvenni azokat, amik az osztály adattagjai (pl. a tömb, az elemszám ) használnunk kell a CompareTo() metódust a két tömbelem összehasonlításához RENDEZETT GENERIKUS HALMAZ OSZTÁLY KÉSZÍTÉSE Alakítsuk át a feladatban elkészített programunkat (a Halmaz osztály felhasználásával készített ötös- és hatoslottó-sorsoló programot)! Tegyük a Halmaz osztályt generikussá (típussal paraméterezhetővé), és alakítsuk át úgy, hogy az elemeket rendezetten tárolja! Módosítsuk a főprogramot az új osztálynak megfelelően! 102
103 ábra Lottósorsolás rendezett számokkal Első lépésben a Halmaz osztályunkat kell átalakítanunk. Generikus típust szeretnénk belőle csinálni, azaz el akarjuk látni egy T típusparaméterrel. Ugyanakkor azt szeretnénk, hogy az elemeket rendezetten lehessen benne tárolni, ezért erre a T típusra megszorítást kell életbe léptetnünk. Az új osztály definíciója a következőképpen alakul: class Halmaz<T> where T:IComparable<T> private T[] elemek; A konstruktoron is változtatnunk kell. A tömb inicializálásakor is használnunk kell a T-t: public Halmaz() n = 0; elemek = new T[MAX]; Nagyobb falat az ElemeE() metódus átalakítása. A kisebbik probléma, hogy a fejlécében a típust T-re kell cserélnünk. A nagyobbik, hogy a lineáris keresés helyett logaritmikust kell használnunk. Valósítsuk meg ezt az Elméleti összefoglalóban található algoritmus segítségével! public bool ElemeE(T elem) bool l = false; int e = 0; int u = n - 1; int i = 0; Az algoritmushoz képest egyetlen dolgon kellett változtatnunk. A bekeretezett sorra azért van szükség, mert az i értékét még fel fogjuk használni, és emiatt a ciklus előtt kell deklarálni, és kezdőértéket is kell neki adni. while (!l && e <= u) i = (e + u) / 2; Idáig ugyanaz, mint az algoritmus. Az utolsó algoritmusban használhatjuk a / műveletet, mert ebben az esetben egész osztást jelent. Ezt követően azonban a háromirányú elágazást kell megvalósítanunk. Ehhez meg kell vizsgálnunk a CompareTo() által visszaadott értéket. Amennyiben az negatív, akkor az a kisebb, amihez hasonlítunk, ha pozitív, akkor az, amit hasonlítunk, egyébként már csak egyenlők lehetnek. 103
104 int x = elemek[i].compareto(elem); if (x < 0) e = i + 1; else if (x > 0) u = i - 1; else l = true; Ne felejtsük el, hogy az elem törlése felhasználja a keresés eredményét, ezért a logikai változó értékének visszaadása előtt a talalt változó értékét is be kell állítanunk, hasonlóan a korábbi lineáris kereséshez. talalt = i; return l; Ezzel implementáltuk a logaritmikus keresést. Most következik a rendezés integrálása. A 12. fejezetben már elkészítettük, ezért onnan fel tudjuk használni. Először másoljuk át a Csere eljárást! Annyit kell változtatni csak rajta, hogy immáron T típusú elemekkel kell dolgoznia. private static void Csere(ref T x, ref T y) T s = x; x = y; y = s; Itt van szerepe annak, hogy a metódust statikusként írtuk meg, itt is úgy használjuk. A statikus metódus azt jelenti, hogy az adott metódus az osztályhoz tartozik hozzá, egyetlen példányban jön létre a memóriában, nem annyiszor, ahány objektumot létrehozunk. Éppen ezért nem tud hozzáférni az osztály nem statikus adattagjaihoz (ilyeneket mi még nem használtunk). Általában olyan metódusokat szoktunk így megírni, amelyek csak segítik az osztály munkáját, például itt a Csere eljárás, vagy a következő fejezetben ilyenek lesznek az operátorok. Mivel a másik két metódusnak hozzá kell férnie a tömbhöz, meg az elemszámához, ezért őket nem vehetjük fel statikusként, így a static kulcsszót törölnünk kell a fejlécből. Ilyen okok miatt az említett adatokat nem kell a metódus fejében szerepeltetnünk. Ugyanakkor nem szeretnénk, hogy ezeket a metódusokat a főprogramból is meg lehessen hívni, ezért mindenképpen privátként vegyük fel őket! A minimumkiválasztásban szerepel egy összehasonlítás, ezért ismét a CompareTo() függvényt kell használnunk. Ennek most csak egy ága van, ezért a hívást beágyazhatjuk az elágazás feltételébe. private void MinKiv(int i, out int ind) ind = i; for (int j = i + 1; j < n; j++) if (elemek[j].compareto(elemek[ind]) < 0) ind = j; A rendezést már könnyen meg tudjuk írni. A fejlécből eltávolítjuk a paramétereket, így ez egy paraméter nélküli eljárássá alakul át. Ezen kívül csak a tömbhivatkozást kell átírnunk. 104
105 private void Rendez() for (int i = 0; i < n - 1; i++) int ind; MinKiv(i, out ind); if (i!= ind) Csere(ref elemek[i], ref elemek[ind]); Mivel itt nagyon gyakran szinte teljesen rendezett tömbön végzünk rendezést, ezért célszerű a csere előtt megvizsgálni, hogy van-e értelme, azaz a megcserélendő elemek indexe különbözik-e. A fenti kódban ezt a vizsgálatot jelöltük. Egészítsük ki a Rendez() meghívásával a Halmazba és a Halmazbol metódusokat! public void Halmazba(T elem) if (!ElemeE(elem) && n < MAX) elemek[n++] = elem; Rendez(); public void Halmazbol(T elem) if (ElemeE(elem)) elemek[talalt] = elemek[n - 1]; n--; Rendez(); Már csak a főprogramon kell minimális mértékben módosítani, hogy kompatibilis legyen az új Halmaz osztállyal. Példányosításkor adjuk meg az int típust paraméterként: Halmaz<int> h = new Halmaz<int>(); Ha mindent jól csináltunk, akkor a lottósorsolás számait most már emelkedő számsorrendben látjuk. 105
106 EGYSZERŰBB FELADAT Kódold be az alábbi Erathosztenészi szita algoritmusát C# nyelven (p legyen egy egész elemeket tartalmazó halmaz). Az algoritmus lefutása után a halmaz elemeit írasd ki a képernyőre! Milyen számokat kapsz eredményül? p üres Ciklus i=2-tıl 255-ig p halmazba (i) Ciklus vége i:=1 Ciklus amíg i<255 i:=i+1 Ha i eleme p Akkor j:=1 Ciklus amíg i*j<255 j:=j+1 p halmazból (i*j) Ciklus vége Elágazás vége Ciklus vége Tipp: a Halmaz osztály definíciójában a tömb maximum elemszámát növeljük meg 255-re! ÖSSZETETTEBB FELADAT Halmaz felhasználásával oldd meg az alábbi feladatot! Egy beolvasott szövegben számold meg, hány magyar magánhangzó van! Segítségképpen az MH.TXT állományban megadjuk az összes kis- és nagybetűs magyar magánhangzót. Tipp: ebben az esetben a halmazt char típussal érdemes példányosítani. 106
107 OPERÁTOROK Ennek a témakörnek az a célja, hogy a már elkészített osztályaidat ki tudd egészíteni különböző bináris operátorokkal, így sokkal természetesebb, egyszerűbb szintaktikával tudod őket a későbbiekben használni. Például a Halmaz osztályt jóval egyszerűbben meg tudjuk valósítani, ha megírjuk a szokásos halmazműveleteket, méghozzá eleve úgy, hogy azok rendezett tömbön is hatékonyan működjenek. ELMÉLETI ÖSSZEFOGLALÓ Operátor: speciális nevű függvény, amelyet a matematikában megszokott szintaxis szerint tudunk használni. Általában infix operátorokat használunk (ez azt jelenti, hogy a műveleti jel az operandusok között található). "Hagyományos" függvénnyel: x = Plusz(a,b); Operátorral: x = a + b; Sokkal természetesebb szintaxist használhatunk! Egészítsük ki a Halmaz<T> osztályunkat a megszokott operátorokkal! A + jelentse az uniót, a * pedig a metszetet, a - legyen a különbség! Írjuk meg az algoritmusokat úgy, hogy kihasználják a rendezettséget, tehát a rendezett tömböket összefésülje! Az operátorok mindig statikus metódusok, mert az osztályhoz tartoznak, és függvények, mert osztály típusú értéket adnak vissza. Példa (két halmaz uniója operátorral): public static Halmaz<T> operator + (Halmaz<T> a, Halmaz<T> b) visszatérési érték operandus 1 operandus 2 Rendezett tömbök uniója: Adottak az A[0..n-1] és B[0..m-1] vektorok, amelyek elemei növekvően rendezettek. Állítsuk elő a C[0..k-1] vektort, amelybe kerüljenek azok az elemek, amelyek vagy az A, vagy a B vektorban benne vannak (amelyek mindkettőben, azok csak egyszer)! Algoritmus: k:=0; i:=0; j:=0 Ciklus amíg (i<n) és (j<m) Ha A[i]<B[j] akkor C[k]:=A[i]; i:=i+1 Különben Ha A[i]>B[j] akkor C[k]:=B[j]; j:=j+1 Különben C[k]:=A[i]; i:=i+1; j:=j+1 k:=k+1 Ciklus vége. Ciklus amíg (i<n) C[k]:=A[i]; i:=i+1; k:=k+1 Ciklus vége. Ciklus amíg (j<n) C[k]:=B[j]; j:=j+1; k:=k+1 Ciklus vége. metódus neve mindkettőben van maradék elemek A-ból maradék elemek B-ből csak az A-ban van csak a B-ben van 107
108 Rendezett tömbök metszete: Adottak az A[0..n-1] és B[0..m-1] vektorok, amelyek elemei növekvően rendezettek. Állítsuk elő a C[0..k-1] vektort, amelybe kerüljenek azok az elemek, amelyek az A és a B vektorban is benne vannak! Algoritmus: k:=0; i:=0; j:=0 csak az A-ban van Ciklus amíg (i<n) és (j<m) Ha A[i]<B[j] akkor i:=i+1 csak a B-ben van Különben Ha A[i]>B[j] akkor j:=j+1 Különben C[k]:=A[i]; i:=i+1; j:=j+1; k:=k+1 Ciklus vége. mindkettőben van A többi halmazművelet is hasonlóképpen megírható. Például: A-B (azok az elemek, amelyek A- ban benne vannak, de B-ben nincsenek). Ilyenkor csak az első ágban kell az eredménybe elemet tenni, illetve a végén az unióból ismert első ciklusra van szükség. Rendezett tömbök különbsége: k:=0; i:=0; j:=0 csak az A-ban van Ciklus amíg (i<n) és (j<m) Ha A[i]<B[j] akkor C[k]:=A[i]; i:=i+1; k:=k+1 Különben Ha A[i]>B[j] akkor j:=j+1 Különben i:=i+1; j:=j+1 Ciklus vége. Ciklus amíg (i<n) C[k]:=A[i]; i:=i+1; k:=k+1 Ciklus vége. mindkettőben van maradék elemek A-ból csak a B-ben van OPERÁTOROK ÍRÁSA Alakítsuk át a Halmaz osztályt úgy, hogy a halmazba beillesztést és a törlést is operátorok segítségével valósítsa meg! Készítsük el az unió, metszet és a különbség műveleteket, a fenti, rendezett tömbökre vonatkozó algoritmusok felhasználásával. Készítsünk mintaprogramot, amely két halmazt feltölt véletlenszámokkal, majd teszteli az említett műveleteket, és az eredményüket ábrázolja Venn-diagramokkal! 108
109 ábra Halmazműveletek grafikus ábrázolással A Halmaz osztályunkat alaposan át kell alakítanunk, ha egyszerű szintaktikával, operátorok segítségével szeretnénk megvalósítani. Készítsünk egy új Windows Forms projektet, mentsük el, és adjuk hozzá az előző fejezetben elkészített, generikus Halmaz osztályunkat. Töröljük ki belőle a Halmaz0ba, Halmazból műveleteket, valamint a Rendez, MinKiv és Csere metódusokat! Ezekre ugyanis a továbbiakban nem lesz szükség. Helyettük írjuk meg az operátorok kódját. Kezdjük az unió művelettel! A metódus fejléce már az Elméleti összefoglalóban is szerepel, de itt is megadjuk: public static Halmaz<T> operator +(Halmaz<T> a, Halmaz<T> b) Első lépésben létrehozzuk az eredményhalmazt, amelyet majd a metódus végén visszatérési értékként vissza fogunk adni: Halmaz<T> c = new Halmaz<T>(); Szükségünk van két indexre, amelyek azt jelölik, hogy a két tömbben meddig sikerült eljutnunk. int i = 0; int j = 0; Az Elméleti összefoglalóban található algoritmus szerint még az új tömb elemszámát is ki kellene nulláznunk, azonban ezt megteszi az imént hívott konstruktor, tehát ezzel nem kell foglalkoznunk. A következő ciklus addig megy, amíg egyik tömbnek sem értünk a végére. A tömbök elemszáma a Halmaz osztály privát mezője (n), de az operátorunk képes arra, hogy ennek az értékét kiolvassa, így a ciklus feltétele a következőképpen alakul: while (i < a.n && j < b.n) Most a tömb elemeinek összehasonlítása következik. Mivel ez is generikus osztály, ezért muszáj a CompareTo() függvényt használni, az előző fejezetben megismert módon: int x = a.elemek[i].compareto(b.elemek[j]); Az első ág akkor teljesül (a rendezettség miatt), ha az adott elem csak az A halmazban van. Unió esetén ekkor el kell helyezni az eredményhalmazban, és az A-hoz tartozó indexet kell növelni: 109
110 if (x < 0) c.elemek[c.n] = a.elemek[i++]; A második ág esetén ugyanez történik, csak a B halmazzal: else if (x > 0) c.elemek[c.n] = b.elemek[j++]; A harmadik ágban a két elem egyenlő, ezért csak az egyikből kell betenni az eredménybe, és mindkét indexet növelni kell: else c.elemek[c.n] = a.elemek[i++]; j++; Ne felejtsük el, hogy mivel mindhárom ágban tettünk elemet a C halmazba, ezért a ciklus vége előtt az elemszámát mindenképpen növelnünk kell: c.n++; Abban az esetben, ha unióról beszélünk, akkor azokra az elemekre is szükség van, amik csak az egyik halmazban vannak benne. A ciklus leáll, ha bármelyik elemek tömb végére érünk, tehát valamelyikben maradnak még elemek, amelyeket az eredménytömbbe át kell pakolni. Ezért kell még két ciklus, amelyik ezt megteszi (a kettő közül mindig csak az egyik fog lefutni, de nem tudhatjuk, mikor melyik). while (i < a.n) c.elemek[c.n++] = a.elemek[i++]; while (j < b.n) c.elemek[c.n++] = b.elemek[j++]; Mivel ez egy függvény, vissza kell adnunk egy értéket, amely ebben az esetben egy objektum lesz. Az összeadás eredménye a c Halmaz típusú objektum, tehát őt kell visszaadni: return c; Ezzel elkészült az első operátorunk. A metszet lényegesen rövidebb, az előző levezetés és az Elméleti összefoglalóban található algoritmus alapján könnyen megírható. Arra kell csak figyelni, hogy az elágazás első két ágában csak az indexet növeljük, és az eredményhalmaz elemszámát pedig csak a harmadik ágban: public static Halmaz<T> operator *(Halmaz<T> a, Halmaz<T> b) Halmaz<T> c = new Halmaz<T>(); int i = 0; int j = 0; while (i < a.n && j < b.n) int x = a.elemek[i].compareto(b.elemek[j]); if (x < 0) i++; else if (x > 0) j++; else c.elemek[c.n++] = a.elemek[i++]; j++; return c; Mivel ez egy metszet, így a kimaradt elemekkel nem kell foglalkoznunk, ezért az utolsó két ciklus elhagyható. 110
111 Írd meg a különbséget az eddigiek és az Elméleti összefoglaló algoritmusa alapján! Igen ám, de hogyan tudunk elemet tenni a halmazba? Természetesen az unió (+) művelet segítségével. De amennyiben egyetlen elemet szeretnénk beletenni, szükségünk van egy olyan paraméteres konstruktorra, amely létrehoz egy egyelemű halmazt, hiszen azt kell utána az unió művelettel hozzáadni a meglévő halmazunkhoz. Az új konstruktort úgy kell megírni, hogy az futtassa le a régit, majd ezt követően helyezzen el egy elemet a halmazban: public Halmaz(T elem) : this() elemek[n++] = elem; Az új konstruktor feje után írt :this() azt jelenti, hogy az eredeti, paraméter nélküli konstruktornak is le kell futnia, mielőtt ezt végrehajtja. Most már minden készen áll ahhoz, hogy használatba tudjuk venni a megújult Halmaz osztályunkat. Tervezzük meg az alkalmazás kezelőfelületét a ábrán láthatóhoz hasonlóan. A mintaprogramban a halmazok maximális elemszámát NumericUpDown vezérlővel is beállíthatjuk, amelynek előnye, hogy egy megadott minimális és maximális érték között lehet léptetni a számlálót. A rajzolás pedig PictureBox komponens segítségével történik, amelyen a halmazokat grafikusan meg tudjuk jeleníteni. Állítsuk be a kép méretét (a példában 500*300 pixel)! Kezdjük el megírni a gombra kattintás eseménykezelőjét! A rajzoláshoz először létre kell hoznunk egy bitkép (Bitmap) típusú objektumot, majd ehhez egy grafika típusút (Graphics), és a kettőt össze kell kapcsolnunk. Bitmap bmp = new Bitmap(pictureBox1.Width, picturebox1.height); Graphics g = Graphics.FromImage(bmp); A Bitmap objektum konstruktorának meg kell adni paraméterként a PictureBox komponens szélességét és magasságát. A g pedig tulajdonképpen egy vászon, amelyre rajzolni tudunk. Első lépésben letöröljük a megadott háttérszínnel: g.clear(color.cornflowerblue); Szükségünk lesz még egy toll (Pen) objektumra is, amellyel rajzolni tudunk. Ennek létrehozásakor meg kell adnunk a színét és a vastagságát: Pen p = new Pen(Color.Black, 2); Rajzoljunk két kört, amelyet a g objektum DrawEllipse() metódusával tehetünk meg. Paraméterként megadjuk a tollat, valamint a kört befoglaló téglalap bal felső sarkának két koordinátáját, illetve szélességét és magasságát: g.drawellipse(p, 1, 1, 298, 298); g.drawellipse(p, 200, 1, 298, 298); Folytassuk a halmazok elkészítésével! A NumericUpDown vezérlő Value tulajdonsága tárolja az aktuálisan beállított értéket, azonban ezt csak egy típuskényszerítés után tudjuk értékül adni egy int típusú változónak: 111
112 int maxa = (int)numericupdown1.value; int maxb = (int)numericupdown2.value; Szükségünk lesz egy Random objektumra is: Random r = new Random(); Hozzunk létre egy új Halmaz objektumot! Halmaz<int> A = new Halmaz<int>(); Következik a halmaz elemekkel való feltöltése. Amennyiben garantálni szeretnénk, hogy pontosan annyi elemmel töltsük fel a halmazokat, amennyit a felhasználó megadott, akkor a megszokott hátultesztelős ciklusunkat kell használnunk. Viszont nincsen Halmazba művelet, ezért helyette majd mást kell csinálnunk. int x; for (int i = 1; i <= maxa; i++) do x = r.next(1, 41); while (A.ElemeE(x)); Ezt követően kellene az új elemet a halmazba tenni. Mivel megírtuk a + operátort, ezért ezzel automatikusan használhatjuk a += műveletet is. És van olyan konstruktorunk, amellyel egyelemű halmazt hozhatunk létre az x változó értékéből! Tegyük hát meg, és fűzzük hozzá a halmazunkhoz! A += new Halmaz<int>(x); Ezzel fel tudjuk tölteni a halmazunkat, kényelmesen, viszonylag egyszerű szintaxist használva. Ugyanezzel a módszerrel írd meg a B halmaz feltöltését is! Írjuk ki az A és B halmaz tartalmát a kép alá helyezett címkékre: label3.text = "A = " + A.Kiir(); label4.text = "B = " + B.Kiir(); Hála az operátoroknak, nagyon egyszerűen elkészíthetjük a képre kiírandó halmazokat. Szükségünk lesz az A\B, B\A és A B halmazokra: Halmaz<int> AminuszB = A - B; Halmaz<int> BminuszA = B - A; Halmaz<int> AmetszetB = A * B; Már csak ki kéne írnunk a képre ezek tartalmát, amelyet a DrawString() metódussal tehetünk meg. Elég sok paraméterre van szükség: a kiírandó szövegre, egy betűtípusra (amit mi a Formra helyezett címkéről veszünk), egy ecsetre (amelynek meg kell adnunk a színét), illetve az elhelyezendő szöveg bal felső sarkának koordinátáit: SolidBrush br = new SolidBrush(Color.Black); g.drawstring(aminuszb.kiir(), label1.font, br, 13, 150); g.drawstring(ametszetb.kiir(), label1.font, br, 213, 150); g.drawstring(bminusza.kiir(), label1.font, br, 323, 150); Utolsó lépésként az elkészült képet kirakjuk a PictureBoxba: picturebox1.image = bmp; 112
113 Elkészültünk a halmazos programunkkal. Aránylag szemléletesen mutatja a halmazműveletek eredményeit, és azt is láthatjuk, hogy az új osztály kevesebb kóddal többet tud, mint a régi, és az operátorok sokkal természetesebben használhatóvá teszik. ÖSSZETETTEBB FELADAT Egészítsd ki a feladatban elkészített Tört osztályt a négy alapműveletet jelentő operátorral (+, -, *, /)! Készíts mintaprogramot, amely két törttel végez műveleteket, és ezeket használja fel! ábra Műveletek törtekkel 113
114 ÖRÖKLŐDÉS Ennek a témakörnek az a célja, hogy a már elkészített osztályaidból tudj újakat származtatni. Ez azért jó, mert egyrészt amit egyszer már megírtál, nem kell még egyszer megírni, másrészt pedig sok olyan feladat van, ahol az általános speciális megközelítés nagyban leegyszerűsíti a dolgunkat. Például készíthetünk egy Jármű osztályt, amely összefogja a közös tulajdonságokat és műveleteket, és ebből származtathatunk speciális osztályokat: Autó, Hajó, Repülő, stb. ELMÉLETI ÖSSZEFOGLALÓ Öröklődésről beszélünk, ha egy osztályból származtatunk egy új osztályt. Amiből származtatunk, azt ősosztálynak, a származtatott osztályt pedig utódosztálynak nevezzük. Az utód örökli az ős összes adattagját, tulajdonságát és metódusát. Természetesen az utódnak lehetnek új adattagjai, tulajdonságai, metódusai. Arra is van lehetőség, hogy felülírjuk az ősosztály metódusait. Problémát okoz, hogy a privát mezőket csak abból az osztályból érjük el, amiben deklaráljuk őket, még a származtatottból sem! A megoldás: a védett (protected) mező használata. Ez ugyanúgy viselkedik, mint a privát (tehát kívülről nem elérhető), viszont az utódosztályból is használható. Példa (ősosztály): class Kor védett mező protected int x, y; protected int r; public Kor(int ax, int ay, int ar) public void Rajzol(Graphics g) származtatva a Kor osztályból Példa (utódosztály): class Ellipszis : Kor plusz mező paraméteres konstruktor protected int R; public Ellipszis(int ax, int ay, int ar, int ar) public new void Rajzol(Graphics g) az ősosztály metódusát felülírja! Amennyiben azt szeretnénk, hogy az utód metódusa felülírja az ős metódusát, használjuk a new kulcsszót! A két metódus neve és paraméterei meg kell, hogy egyezzenek! Vannak azonban olyan metódusok, amelyeket a minden osztály ősének számító Object ősosztálytól öröklünk, ezeket másképpen kell felüldefiniálni. Például minden osztály örökli a ToString() metódust, amely az ősosztályban virtuális metódusként van deklarálva. Ez azt jelenti, hogy az ősosztályban nincs megvalósítva, arra "vár", hogy az utódban megvalósítsák. Ebben az esetben az override kulcsszót kell alkalmaznunk. Régi fejléc: public string Kiir() Új fejléc: public override string ToString() Mi a helyzet akkor, ha meg szeretnénk hívni az ősosztálybeli megvalósítást is, mielőtt az utódbeli tevékenységet elvégeznénk? 114
115 Például az Ellipszis osztály konstruktorának hívásakor célszerű meghívni az ős (Kor) osztály konstruktorát, amely értéket ad az onnan örökölt mezőknek. Megoldás: public Ellipszis(int ax, int ay, int ar, int ar) : base(ax, ay, ar) az ősosztály konstruktorát hívja! Tegyük fel, hogy az általunk megírt Tort osztályt kompatibilissé szeretnénk tenni a halmazunkkal! Ehhez az kell, hogy legyen ToString() és CompareTo() metódusa az osztálynak. A ToString() nem probléma (lásd fentebb) A CompareTo() használatához viszont a Tort osztályunkat származtatnunk kell: class Tort : IComparable<Tort> Így már lehet az osztályunknak ilyen metódusa. AZ ÖRÖKLŐDÉS MEGVALÓSÍTÁSA Készítsünk egy nagyon egyszerű rajzolóprogramot! Írjunk hozzá egy Kor osztályt, ami a megadott középpontú és sugarú kört rajzolja meg! Származtassunk belőle egy Ellipszis osztályt, amely kiegészítve az őstől kapottakat, ellipszist rajzol! Készítsünk mintaprogramot, amellyel kipróbálhatjuk az osztályokat! ábra Ellipszis és kör rajzolása Először meg kell írnunk a Kor osztályt, ami az ős szerepét fogja a továbbiakban betölteni. Az osztálynak három mezője lesz: a kör középpontja (x és y koordináta), valamint a sugara (r). Mivel öröklést szeretnénk a későbbiekben, ezért nem használhatunk privát, csak védett (protected) adattagokat. Készítsünk hozzá egy paraméteres konstruktort, amely a paraméterként átadott adatokat értékül adja a mezőknek. 115
116 class Kor protected int x, y; protected int r; public Kor(int ax, int ay, int ar) x = ax; y = az; r = ar; Szükségünk van egy rajzoló metódusra is. Paraméterként egy Graphics típusú objektumot adunk át, amelyre rajzolhatunk, de emiatt a Kor.cs állományba fel kell venni a grafikához használt névteret: using System.Drawing; A Rajzol metódusban deklarálunk egy toll objektumot, majd rajzolunk egy kört. Figyelnünk kell arra, hogy a DrawEllipse() metódusnak a befoglaló téglalap bal felső sarkát kell megadni, tehát az x és az y koordinátából le kell vonnunk a sugarat, illetve hogy a téglalap szélessége és magassága pontosan a sugár kétszerese. public void Rajzol(Graphics g) Pen p = new Pen(Color.Black, 2); g.drawellipse(p, x - r, y - r, 2 * r, 2 * r); Készen vagyunk a Kor osztállyal. Ebből nagyon egyszerűen tudunk származtatni egy Ellipszis osztályt, a Kor felhasználásával: class Ellipszis : Kor Mivel a származtatás miatt az új osztályunk rendelkezik x, y, r mezővel, ezért itt már csak egy másik sugarat (R) kell felvennünk. protected int R; A konstruktor megírásánál is egyszerűsödik a dolgunk. Az Elméleti összefoglalóban látható módon meg kell hívnunk az ősosztály konstruktorát három paraméterrel, és így az új konstruktorban csak a negyedik paraméternek kell értéket adnunk: public Ellipszis(int ax, int ay, int ar, int ar) : base(ax, ay, ar) R = ar; Felül kell írnunk az ősosztály Rajzol() metódusát, mert itt a másik sugárral kell számolnunk. Emiatt a new kulcsszót kell használnunk az eljárás fejében: public new void Rajzol(Graphics g) Pen p = new Pen(Color.Black, 2); g.drawellipse(p, x - r, y - R, 2 * r, 2 * R); Az Ellipszis osztályunk készen is van. Írjuk meg hozzá a programot! A felhasználói felületet tervezzük meg a ábrának megfelelően (itt is egy 500*300 pixel méretű PictureBox komponenst használtunk, a beállított háttérszín neve BlanchedAlmond), a számok bevitelére pedig NumericUpDown vezérlőt alkalmazunk. Először is, a Form1() konstruktor fölé vegyünk fel két objektumot, amelyekre szükség lesz a grafikához: 116
117 Graphics g; Bitmap bmp; A Form1() konstruktorban fogjuk ezeket az objektumokat létrehozni, az alkalmazás indulásakor: public Form1() InitializeComponent(); bmp = new Bitmap(pictureBox1.Width, picturebox1.height); g = Graphics.FromImage(bmp); A Törlés gombra kattintáskor letöröljük a rajzot, és megjelenítjük a PictureBoxon: g.clear(color.blanchedalmond); picturebox1.image = bmp; A Rajzol gombra kattintáskor először is négy változóba mentsük ki a NumericUpDown vezérlők értékeit: int x = (int)numericupdown1.value; int y = (int)numericupdown2.value; int r1 = (int)numericupdown3.value; int r2 = (int)numericupdown4.value; Amennyiben a felhasználó a Kört választotta ki, akkor létrehozunk egy Kor objektumot a megadott paraméterekkel, és meghívjuk a Rajzol() metódust: if (radiobutton1.checked) Kor kor = new Kor(x, y, r1); kor.rajzol(g); Ellenkező esetben Ellipszist hozunk létre: else Ellipszis ell = new Ellipszis(x, y, r1, r2); ell.rajzol(g); Végül a képet megjelenítjük: picturebox1.image = bmp; A mintaprogramhoz hasonlóan célszerű úgy csinálni, hogy ha kört választ a felhasználó, akkor a negyedik NumericUpDown vezérlőt nem árt letiltani. A második rádiógomb OnChange eseménykezelőjébe írjuk be: numericupdown4.enabled = radiobutton2.checked; Ez az egyszerű kis program jól példázza az öröklődésben rejlő lehetőségeket. A TÖRT OSZTÁLY KOMPATIBILISSÉ TÉTELE A HALMAZZAL Egészítsük ki a Tört osztályunkat, annak érdekében, hogy a már megírt, rendezett Halmaz osztályunkkal kompatibilis legyen. Készítsünk egy mintaprogramot, amellyel ezt tesztelni tudjuk! ábra Törtek halmaza 117
118 Készítsünk egy új programot, adjuk hozzá a Halmaz és a Tort osztályunk legújabb verzióját! Minimális átalakításokkal kompatibilissé tehetjük őket egymással. Először is mindkét osztályban cseréljük le a Kiir() metódust ToString()-re, az Elméleti összefoglalóban látható módon: public override string ToString() Ahhoz, hogy a Tort osztályunk kompatibilis lehessen a Halmazzal, megfelelően származtatnunk kell: class Tort : IComparable<Tort> Majd ezek után meg kell írnunk a CompareTo() metódust. Korábban már többször használtuk, ezért tudjuk, hogy paraméterként egy másik, ugyanolyan típusú objektumot vár, és egy egész számot ad vissza értékül. Amennyiben az negatív, akkor az a kisebb, amelyik objektumból hívtuk, ha pozitív, akkor a paraméterként átadott, egyébként pedig egyenlők. A fejléc tehát a következőképpen alakul: public int CompareTo(Tort masik) Két törtet úgy tudunk összehasonlítani, hogy közös nevezőre hozzuk őket, majd a számlálójukat összehasonlítjuk. Ezért létrehozunk két új Tort objektumot, és bővítjük őket a közös nevezőre, ami a két összehasonlításra váró tört nevezőjének szorzata (legegyszerűbb esetben). Bővítsük ki tehát a hívó objektum számlálóját és nevezőjét a másik tört nevezőjével: Tort x = new Tort(); x.sz = sz * masik.n; x.n = n * masik.n; Illetve a másik törtet a hívó objektum nevezőjével: Tort y = new Tort(); y.sz = masik.sz * n; y.n = masik.n * n; Most már össze tudjuk hasonlítani a két tört számlálóját, és az ennek megfelelő eredményt adjuk vissza: if (x.sz > y.sz) return 1; else if (x.sz < y.sz) return -1; else return 0; A Tort osztályt ezzel kompatibilissé tettük a Halmaz osztályunkkal, azaz mostantól törteket is tehetünk a halmazba. Helyezzük el a Formon a megfelelő komponenseket, és írjuk meg a gomb eseménykezelőjét! Halmaz<Tort> h = new Halmaz<Tort>(); int max = (int)numericupdown1.value; Random r = new Random(); Tort t; Figyeljük meg, hogy minden további nélkül tudjuk példányosítani a Halmaz osztályt a Torttel, pontosan úgy, mint ha beépített típus lenne. Ezt követően kinyerjük a NumericUpDown értékét, létrehozunk egy Random objektumot, illetve deklarálunk egy Tortet. 118
119 Korábbi fejezetekből már ismerős lehet az a hátultesztelő ciklus, amely nem engedi, hogy olyan elem kerüljön a halmazba, amely már benne van, így biztosítja, hogy annak tényleg a megadott számú eleme legyen. Csak a típusokat kell átírni kicsit: for (int i = 1; i <= max; i++) do int sz = r.next(1, 21); int n = r.next(1, 21); t = new Tort(sz, n); while (h.elemee(t)); h += new Halmaz<Tort>(t); A belső ciklusban két véletlenszámot generálunk, majd létrehozunk vele egy új Tort objektumot, amit még a külső ciklus előtt deklaráltunk. Amennyiben sikerült tényleg új törtet készíteni, azt rögtön be is tesszük a halmazba, a szokásos módon. Végül kiírjuk a halmaz elemeit a címkére: label2.text = "Törtek:\n" + h.tostring(); Az így elkészült Tort és Halmaz osztályok egymással teljesen kompatibilisek. ÖSSZETETTEBB FELADAT Készíts egy Negyzet nevű osztályt, amely tárolja a négyzet sarkának bal felső koordinátáit, illetve a négyzet oldalhosszát, és ki is tudja azt rajzolni! Származtass belőle egy Teglalap osztályt, ami ugyanezt tudja, csak téglalappal (természetesen ott tárolni kell egy másik oldalhosszt is)! Egészítsd ki a feladatban elkészített programot, hogy tudjon négyzetet és téglalapot is rajzolni! Tipp: négyzetet, téglalapot a DrawRectangle() metódussal lehet. 119
120 ADATSZERKEZETEK: SOR Ennek a témakörnek az a célja, hogy az eddigi ismeretek birtokában megtanuld, hogyan kell új adatszerkezetet készíteni, és azt használatba venni. Ebben és a következő fejezetben két olyan adatszerkezetet valósítunk meg, amelyet számos programozási és matematikai feladat megoldására használhatunk. ELMÉLETI ÖSSZEFOGLALÓ Amikor egy adott feladatot szeretnénk minél hatékonyabban megoldani, el kell döntenünk, hogy az adatok tárolásához milyen adatszerkezetet veszünk igénybe. Az eddigi adatszerkezeteinket (vektor, rekord, mátrix) a fejlesztői környezet biztosította. Most itt az ideje, hogy magunk is megírjunk néhányat! Tulajdonképpen már írtunk is (Halmaz) Egy adatszerkezethez három dolog tartozik hozzá: Típusértékhalmaz: milyen típusú elemeket tárol. Szerkezet: hogyan épül fel más típusú elemekből. Műveletek: milyen műveletei vannak. Adatszerkezetet generikus osztállyal tudunk megvalósítani. Ekkor: a generikusság miatt szinte minden típusú elemet tudunk benne tárolni általában tömböt használunk a megvalósításhoz a publikus metódusok lesznek az adott típus műveletei. Sor adatszerkezet: A Sor olyan adatszerkezet, melynek jellemzője, hogy az új elemek a végére kerülnek, míg a következő elemet az elejéről vesszük el. Műveletek: Üres Sor létrehozása Üres-e a Sor? Tele van-e a Sor? (tömbös megvalósítás esetén) Sorba: új elem elhelyezése a sorban Sorból: a sor első elemének kivétele Megvalósítás: se[0..max-1] vektor segítségével. Két mutatót használunk: eleje és vege. Lesz egy hossz, ami tárolja az aktuális elemszámot. A tömb kezelése: e e e ve e MAX-2 MAX-1 k t ut tó e e á y! 120
121 A műveletek megvalósítása: Üres Sor létrehozása: konstruktorban (tömb és változók inicializálása) Üres-e a Sor?: hossz = 0? Tele van-e a Sor? (tömbös megvalósítás esetén): hossz = MAX? Sorba: csak akkor, ha nincs tele a végére elhelyezzük az új elemet megnöveljük a hossz és a vege értékét Sorból: csak akkor, ha nem üres visszaadjuk a sor elején álló értéket az eleje mutatót növeljük a hossz értékét csökkentjük Az eleje és a vege mutatót ciklikusan kell léptetni, azaz ha elérjük a tömb végét, az elejére kell lépnünk! A Sor alkalmazásai: Mivel ugyanúgy működik, mint egy sor a hétköznapi életben, ezért ilyen helyzetek szimulációjára alkalmas (pl. benzinkút, ld. egy későbbi fejezetben). Gráfok szélességi bejárásához használhatjuk. Gráf: csúcsokból és az őket összekötő élekből álló alakzat. Szélességi bejárás: tetszőleges csúcsból indulunk, az összes szomszédját elhelyezzük egy sorban. Amíg a sorban van elem, addig kivesszük az első elemet, és annak még nem meglátogatott szomszédjai kerülnek a sor végére. Szükségünk lesz tehát egy s Sorra, és egy h Halmazra (ebben tároljuk, hogy melyik csúcsot látogattuk már meg). Legyen a pont a bejárás kezdete, pontszám pedig a gráf pontjainak száma. Algoritmus: Létrehoz(s); Létrehoz(h) s.sorba(pont); h.halmazba(pont) Ciklus amíg nem s.ürese s.sorból(pont) Kiír: pont Ciklus i:=1-tıl pontszám-ig Ha VanÉl(pont,i) és nem h.elemee(i) Akkor s.sorba(i); h.halmazba(i) Ciklus vége. Ciklus vége. 121
122 A SOR ADATSZERKEZET MEGÍRÁSA Készítsünk egy Sor osztályt, amellyel meg tudjuk valósítani a Sor adatszerkezetet! Írjunk programot, amely egy irányítatlan gráfot csúcsmátrixos ábrázolással reprezentál, képes éleket hozzáadni, és a Sor és Halmaz osztályok segítségével szélességi bejárást hajt végre a gráfon! ábra Gráfok szélességi bejárása Sor és Halmaz felhasználásával Írjuk meg a Sor adatszerkezetet megvalósító osztályt! Mivel tetszőleges típusú elemből szeretnénk tudni sort készíteni, ezért generikus osztályt kell készítenünk. A sor elemeit egy legfeljebb 200 elemű tömbben fogjuk tárolni, és privát mezőként fel kell vennünk a már említett eleje, vege, hossz adattagokat is. class Sor<T> const int MAX = 200; T[] se; private int eleje, vege, hossz; Írjuk meg a konstruktort! Ebben létrehozzuk a tömböt, a privát mezőket pedig kinullázzuk: public Sor() se = new T[MAX]; eleje = vege = hossz = 0; Két egyszerű metódus következik: ezek segítségével tudjuk ellenőrizni, hogy a sor üres-e, vagy tele van-e. Mindkettőt a tömb aktuális elemszámának ellenőrzésével tudjuk megoldani: public bool UresE() return hossz == 0; public bool TeleE() return hossz == MAX; 122
123 Következik az új elem elhelyezése a sorban. Természetesen először ellenőrizni kell, hogy a tömb nincs-e már tele: public void Sorba(T e) if (!TeleE()) Az új elemet a tömb végére kell elhelyeznünk, majd növeljük a hossz mező értékét. Viszont a vege mezőt ciklikusan kell növelnünk, ami azt jelenti, hogy ha eléri a MAX értékét, akkor ki kell nulláznunk. Ezt ügyesen is megtehetjük: ha a vege+1 értékének MAX-szal való osztási maradékát adjuk értékül a változónak, akkor az éppen 0 lesz, amikor elérjük a MAX értékét. se[vege] = e; hossz++; vege = ((vege + 1) % MAX); A sorból kivételt függvényként kell megírnunk, mert visszaadjuk a kivett értéket, ezért a visszatérési érték típusa T: public T Sorbol() Amennyiben a tömb nem üres, akkor kimentjük egy változóba az első elemet, csökkentjük a hossz értékét, az eleje mezőt pedig ugyanúgy ciklikusan növeljük, mint az előző esetben. Végül visszaadjuk a kimentett értéket: if (!UresE()) T e = se[eleje]; hossz--; eleje = ((eleje + 1) % MAX); return e; Igen ám, de mi van, ha az üres sorra hívjuk meg a metódust? Visszaadott értéknek minden lefutáskor kell lennie! Ilyenkor azt csináljuk, hogy a különben ágban visszaadjuk a T típus alapértelmezett (default) értékét (ez int esetén például a 0). else return default(t); A későbbi feladatokhoz célszerű egy ToString() metódust is készíteni a Sor osztályunkhoz, ha esetleg ki kell íratnunk a sor tartalmát. Amennyiben üres a sor, akkor az Üres. szöveget írjuk ki, egyébként elindulunk az elejétől, és a megszokott ciklikus növelést alkalmazzuk, mindaddig, amíg a végére nem érünk, és közben kiírjuk az elemeket. public override string ToString() if (UresE()) return "Üres."; else string s = ""; int i = eleje; while (i!= vege) s += se[i].tostring() + ", "; i = (i + 1) % MAX; return s; 123
124 GRÁF SZÉLESSÉGI BEJÁRÁSA Gráfok csúcsmátrixos ábrázolása: Legyen graf[0..max-1, 0..MAX-1] egy logikai értékeket tartalmazó mátrix. Legyen graf[i,j] értéke igaz, ha van él az i. és a j. csúcs között, egyébként hamis. Mivel irányított mátrixról van szó, ezért amennyiben graf[i,j] igaz, akkor graf[j,i]-nek is igaznak kell lennie. Ábrázoljuk így a gráfot! A Form konstruktora előtt deklaráljunk egy logikai értékeket tartalmazó mátrixot, és egy konstanst, amelynek értéke legyen eggyel nagyobb, mint ahány csúcsot maximálisan szeretnénk megengedni! Azért célszerű eggyel nagyobb maximum értéket megadni, mert a C# mindent 0-tól számoz, de a gráfoknak általában nincsen 0-s csúcsa. Tehát például amennyiben a MAX értékét 21-nek választjuk, akkor a mátrixot ig indexeljük, ami éppen megfelelő 20 db csúcs tárolására, a 0. sorral és oszloppal pedig nem foglalkozunk. bool[,] graf; const int MAX = 21; A Form konstruktorában hozzuk létre a mátrixot: public Form1() InitializeComponent(); graf = new bool[max, MAX]; Mindenképpen fel kell töltenünk a gráfunkat élekkel. A gombra kattintás után, miután a felhasználó megadta az él kezdőpontját, és végpontját, ellenőriznünk kell, hogy a megadott pontok között van-e már él, mert ha igen, még egyszer nem kell felvennünk (ezt egy logikai mátrix esetén nagyon egyszerű felírni). Ugyanígy ellenőrizzük azt is, hogy olyan élt nem engedünk, aminek a kezdő- és végpontja megegyezik (hurokél). int honnan = (int)numericupdown1.value; int hova = (int)numericupdown2.value; if (!graf[honnan, hova] && honnan!= hova) Ne felejtsük el, hogy a mátrix két elemét (a főátló két oldalán) is igazra kell állítani, amikor új élt adunk hozzá. A hozzáadás eredményét jelenítsük meg a ListBoxban: graf[honnan, hova] = graf[hova, honnan] = true; listbox1.items.add(honnan.tostring() + " <-> " + hova.tostring()); Következik a másik nyomógomb, a szélességi bejárás megvalósítása. Első lépésként olvassuk ki a NumericUpDown vezérlőből a bejárás kezdőpontját, majd hozzuk létre a Sor és a Halmaz objektumokat, mindkettőt egészekkel példányosítva: int pont = (int)numericupdown3.value; Sor<int> s = new Sor<int>(); Halmaz<int> h = new Halmaz<int>(); Adjuk hozzá a kiindulópontot a sorhoz és a halmazhoz is! Figyeljük meg a két osztály használata közötti különbséget! A Halmaz esetében a + operátor rugalmasabbá tette az osztályt, de egy elem hozzáadása esetén kicsit elbonyolítja a szintaxist. 124
125 s.sorba(pont); h += new Halmaz<int>(pont); label4.text = "Bejárás:\n"; Innentől az Elméleti összefoglalóban található algoritmust követjük. A ciklus addig megy, amíg a Sor nem ürül ki: while (!s.urese()) A ciklusban először kiveszünk egy pontot a Sor elejéről, és hozzáfűzzük a ciklus elején kezdőértéket kapott címkéhez: pont = s.sorbol(); label4.text += pont.tostring() + ", "; Ezt követően egy ciklusban végignézzük a szóba jöhető pontokat. Csak azokat kell vizsgálnunk, amelyekbe vezet él a kiolvasott pontból, és még nem jártunk ott, azaz nincsen benne a halmazban. Amennyiben találunk ilyen pontot, akkor elhelyezzük a sorban és a halmazban is, majd folytatjuk a ciklust. for (int i = 1; i < MAX; i++) if (graf[pont, i] &&!h.elemee(i)) s.sorba(i); h += new Halmaz<int>(i); Ezzel elkészültünk a gráf szélességi bejárásával, a Halmaz és a Sor osztály felhasználásával. ÖSSZETETTEBB FELADAT Készíts egy Vasarlo nevű osztályt, amely egy vevő nevét és vásárlásának összegét tartalmazza. Készíts hozzá konstruktort, és egy ToString() metódust, amely kiírja a vásárló nevét és a fizetett összeget. Készíts programot, amely Sor adatszerkezet felhasználásával szimulálja egy bolt forgalmát! A vevők beállnak a sorba, majd fizetnek a kasszánál. Írd ki a képernyőre a sor aktuális állását, illetve azt, hogy éppen ki fizetett! Számold össze az aznapi összbevételt! ábra Pénztár szimuláció Sor adatszerkezettel 125
126 ADATSZERKEZETEK: VEREM Ennek a témakörnek az a célja, hogy az előző fejezethez hasonlóan egy újabb adatszerkezetet valósítsunk meg: a vermet. Az egyik leggyakrabban használt adatszerkezetről van szó: még a processzorban is ezt használják az eljárások visszatérési címeinek tárolására. Ebben a fejezetben elkészítjük a megvalósítását, és példákat látunk az alkalmazására. ELMÉLETI ÖSSZEFOGLALÓ Verem adatszerkezet: A Verem olyan adatszerkezet, melynek jellemzője, hogy az utoljára betett elemet tudjuk legelőször kivenni. Műveletek: Üres Verem létrehozása Üres-e a Verem? Tele van-e a Verem? (tömbös megvalósítás esetén) Verembe (Push): új elem elhelyezése a veremben Veremből (Pop): a verem legfelső elemének kivétele Megvalósítás: ve[0..max-1] vektor segítségével. Itt elég egy mutatót használnunk: teto. Ez a mutató mindig az utolsóként betett elemen áll, ezért kezdőértéke -1. A tömb kezelése: tet MAX-2 MAX-1 A műveletek megvalósítása: Üres Verem létrehozása: konstruktorban (tömb és változók inicializálása) Üres-e a Verem?: teto = -1? Tele van-e a Verem? (tömbös megvalósítás esetén): teto = MAX-1? Verembe (Push): ha nincs tele, akkor tető növelése, és az új elem elhelyezése Veremből (Pop): ha nem üres, akkor elem visszaadása, és tető csökkentése A Verem alkalmazásai: A verem az egyik széles körben használatos adatszerkezet (a CPU-ban is van, abban tárolják például egymásba ágyazott eljáráshívások esetén a visszatérési címeket). Olyan feladatokra alkalmas, amelyekben éppen a fordított sorrendre van szükség, mint ahogy az adat beérkezett. Számos matematikai alkalmazása van: kifejezés helyes zárójelezésének eldöntése, fordított lengyel formára hozás és ilyen formájú kifejezés kiértékelése. 126
127 Fordított lengyel forma: Amikor egy kifejezésben az operátor az operandusok mögött áll, fordított lengyel formájú kifejezésről beszélünk. Példa: eredeti kifejezés: (9+7)*8 fordított lengyel forma: * Előnyei: nem kell zárójelet használni, a kifejezés egyértelmű számítógéppel nagyon könnyű kiértékelni (Verem adatszerkezet használatával). A lengyel forma kiértékelésének algoritmusa: Forrás: Dávid András Pap Gáborné: mikrológia 45 (Adatszerkezetek példatár) Legyen v egy valós számokat tartalmazó Verem, pof pedig egy olyan Sor, amely már tartalmazza a lengyel formára hozott kifejezést. Az algoritmus lényege: Ha operandust (számot) talál a sorban, beteszi a verembe. Ha műveleti jel érkezik, akkor kivesz a veremből két operandust, elvégzi a műveletet, és az eredményt a verembe teszi. A sor kiürülése után a kifejezés értéke a veremben lesz. Algoritmus: Létrehoz(v) Ciklus amíg nem pof.ürese pof.sorból(c) Ha c szám akkor v.verembe(c értéke) Különben v.verembıl(e2); v.verembıl(e1) ertek := Elvégez(c mővelet, e1, e2) v.verembe(ertek) Elágazás vége Ciklus vége v.verembıl(ertek) Lényegesen bonyolultabb a lengyel formára hozás algoritmusa: Forrás: Dávid András Pap Gáborné: mikrológia 45 (Adatszerkezetek példatár) Legyen v itt egy karaktereket tartalmazó Verem, inf egy olyan Sor, amely már tartalmazza az eredeti (infix) formájú kifejezést, pof pedig egy olyan Sor, amelybe előállítjuk a lengyel formára hozott kifejezést. 127
128 Az algoritmus lényege: az inf sort folyamatosan dolgozzuk fel. Amennyiben számot találunk benne, elhelyezzük a pof sorban. A nyitó zárójelet elhelyezzük a veremben. Ha csukó zárójellel találkozunk, akkor a következő nyitó zárójelig mindent kiveszünk a veremből, és átpakoljuk a pof sorba, majd a zárójelpárt eldobjuk. Amennyiben pedig operátort találunk, akkor a nála nem alacsonyabb precedenciájú operátorokat átpakoljuk a veremből a sorba. Az algoritmus végén a veremben maradt műveleti jeleket még át kell tennünk a sorba. Algoritmus: Létrehoz(v); Létrehoz(pof) Ciklus amíg nem inf.ürese inf.sorból(c) Ha c szám akkor pof.sorba(c) Különben Ha c='(' Akkor v.verembe(c) Különben Ha c=')' Akkor NyitoigKivesz(v,pof) v.verembol() Különben NemKisebbeketKivesz(c,v,pof) v.verembe(c) Elágazás vége Ciklus vége Ciklus amíg nem v.ürese pof.sorba(v.verembol()) Ciklus vége. A VEREM ADATSZERKEZET MEGÍRÁSA Készítsünk egy Verem osztályt, amellyel meg tudjuk valósítani a Verem adatszerkezetet! Írjunk programot, amely a felhasználótól bekér egy (infix formájú) matematikai kifejezést, először átalakítja fordított lengyel formájúra, majd azt kiértékeli és az értéket megjeleníti a képernyőn! ábra Lengyel formára hozás és kiértékelés Verem és Sor segítségével Írjuk meg a Verem adatszerkezetet megvalósító osztályt! Mivel tetszőleges típusú elemből szeretnénk tudni sort készíteni, ezért most is generikus osztályt kell készítenünk. A verem elemeit egy legfeljebb 200 elemű tömbben fogjuk tárolni, és privát mezőként fel kell vennünk a teto adattagot. class Verem<T> const int MAX = 200; T[] ve; int teto; 128
129 Írjuk meg a konstruktort! Ebben létrehozzuk a tömböt, a teto értékét pedig -1-re állítjuk: public Verem() ve = new T[MAX]; teto = -1; Két egyszerű metódus következik: ezek segítségével tudjuk ellenőrizni, hogy a verem üres-e, vagy tele van-e. Mindkettőt a teto aktuális állásának ellenőrzésével tudjuk megoldani: public bool Ures() return teto == -1; public bool Tele() return teto == MAX - 1; Következik az új elem elhelyezése a veremben. Természetesen itt is először ellenőrizni kell, hogy a tömb nincs-e már tele. Ha nincs, akkor növeljük a mutatót, és az így mutatott helyre elhelyezzük az új elemet: public void Verembe(T e) if (!Tele()) ve[++teto] = e; A veremből kivételt függvényként kell megírnunk, mert visszaadjuk a kivett értéket, ezért a visszatérési érték típusa most is T: public T Verembol() if (!Ures()) return ve[teto--]; else return default(t); Amennyiben a tömb nem üres, akkor visszaadjuk a teto által mutatott elemet, majd a mutatót csökkentjük. Itt is gondoskodnunk kell arról, ha üres veremre hívjuk meg a metódust, és ezúttal is a T típus alapértelmezett (default) értékét adjuk vissza. Lesz olyan feladatunk, amelyhez szükséges, hogy a verem tetején levő értéket le tudjuk kérdezni, anélkül, hogy kivennénk. Ehhez egy Teto() nevű metódust készítünk, amely majdnem egy az egyben megegyezik az előzővel, azzal a különbséggel, hogy nem tartalmazza a teto mező csökkentését: public T Teto() if (!Ures()) return ve[teto]; else return default(t); A verem tartalmát nem szükséges kiíratni, úgyhogy ezzel elkészült az osztályunk. LENGYEL FORMÁRA HOZÁS ÉS KIÉRTÉKELÉSE Tervezzük meg az alkalmazás kezelői felületét a ábrának megfelelően! A második szövegmező ReadOnly tulajdonságát állítsuk igazra, mert azt most csak kiíratásra fogjuk használni. 129
130 Mielőtt elkezdenénk megírni a gomb eseménykezelő metódusát, szükségünk lesz pár segédeljárásra, amelyeket majd a lengyel formára hozás algoritmusa fog felhasználni. Először is, írjunk meg egy függvényt, amely a paraméterként kapott operátor precedenciájával tér vissza! Legalacsonyabb precedenciával a nyitó és csukó zárójelek rendelkeznek. Utána következik az összeadás és a kivonás, majd a szorzás és az osztás, a legerősebb művelet pedig a hatványozás (amit itt most ^ jellel fogunk jelölni). A függvény visszatérési értéke tehát egy szám, és az előbb megadott sorrendben egyre nagyobbat adunk vissza: private int Prec(char p) int x = 0; switch (p) case '(': case ')': x=1; break; case '+': case '-': x=2; break; case '*': case '/': x=3; break; case '^': x=4; break; return x; Emlékeztető: amennyiben a case utasítás egy ágát üresen hagyjuk, akkor a végrehajtás a következő ágon folytatódik. Így nem kell leírnunk kétszer ugyanazt, mégis azonos utasítás fut le például a nyitó és a csukó zárójelnél is. Következik a NyitoigKivesz() eljárás. Paraméterként egy karaktereket tartalmazó vermet, és egy szintén karaktereket tartalmazó sort kap. Feladata, hogy a nyitó zárójelig mindent kivegyen a veremből, és átpakolja a sorba. private void NyitoigKivesz(Verem<char> v, Sor<char> pof) while (v.teto()!= '(') char op = v.verembol(); pof.sorba(op); Itt már tudjuk használni a korábban megírt Teto() metódust, amellyel csak megnézni tudjuk a verem tetején lévő elemet, kivenni nem. Hasonló a célja a NemKisebbeketKivesz() metódusnak. Paraméterként kap egy műveleti jelet (karakter), valamint egy karaktereket tartalmazó vermet és sort. Feladata, hogy azokat a műveleti jeleket, amelynek a precedenciája nem kisebb, mint a paraméterként kapotté, a veremből a sorba tegye át. Közben azért nem árt figyelni, hogy a verem nem lett-e üres. private void NemKisebbeketKivesz(char muv, Verem<char> v, Sor<char> pof) while (!v.ures() && Prec(muv) <= Prec(v.Teto())) char op = v.verembol(); pof.sorba(op); 130
131 Készen vagyunk a segédmetódusok megírásával, következik a lengyel formára hozás és annak kiértékelése. Első lépésként hozzuk létre az infix alakot tartalmazó sort, és a szövegmezőből karaktereként tegyük át a felhasználó által megadott kifejezést a sorba: Sor<char> inf = new Sor<char>(); for (int i = 0; i < textbox1.text.length; i++) inf.sorba(textbox1.text[i]); Mivel itt a Sort karakterekkel példányosítottuk, ezért a program nem tud a kifejezésben több számjegyből álló számot kezelni! Innentől az Elméleti összefoglalóban található algoritmust követjük. Hozzunk létre egy üres vermet (karakterekkel példányosítva), illetve egy üres sort a postfix alaknak (szintén karakterekből): Verem<char> v = new Verem<char>(); Sor<char> pof = new Sor<char>(); A ciklus addig megy, amíg az infix sorban van elem: while (!inf.urese()) A ciklusban először kiveszünk egy pontot a Sor elejéről: char le = inf.sorbol(); Abban az esetben, ha számjeggyel találkozunk, simán betesszük a postfix sorba. Ne felejtsük el, hogy karakterként kell kezelnünk, azaz jelek közé kell tennünk! if (le >= '0' && le <= '9') pof.sorba(le); Amennyiben nyitó zárójel következik, azt a verembe kell tenni. else if (le == '(') v.verembe(le); Csukó zárójel esetén meghívjuk a NyitoigKivesz() metódust, majd a veremben maradt nyitó zárójelet is kivesszük. else if (le == ')') NyitoigKivesz(v, pof); v.verembol(); Az elágazás utolsó ágában a NemKisebbeketKivesz() eljárást hívjuk, majd az operátort a verembe tesszük: else NemKisebbeketKivesz(le, v, pof); v.verembe(le); Kell még egy ciklus, amellyel a veremben maradt karaktereket áthelyezzük a postfix alak sorába, majd az elkészült kifejezést megjelenítjük a szövegdobozban. while (!v.ures()) pof.sorba(v.verembol()); textbox2.text = pof.tostring(); 131
132 Következik a fordított lengyel formájú alak kiértékelése. Itt is szükség van egy veremre, de az előbbiektől eltérően nem karakterrel, hanem valós típussal kell példányosítani (azért valós, mert az osztáshoz szükség lehet rá). Éppen ezért az új vermet itt v2-nek kell neveznünk. Kell továbbá egy változó, amely az eredményt fogja tárolni. Verem<double> v2 = new Verem<double>(); double ert = 0; A ciklus addig megy, amíg a postfix sor nem üres. A ciklusmagban először kiveszünk egy karaktert a sorból: while (!pof.urese()) char c = pof.sorbol(); Először azt az esetet kell vizsgálni, amikor számjegyet találunk. Ezt karakterről számmá kell konvertálnunk, és így betennünk a v2 verembe. Amennyiben a Convert.ToInt32() metódust használjuk a konvertáláshoz, az a karakter kódját adja vissza. Amennyiben levonjuk belőle a 0 karaktert (vagyis annak kódját), akkor a közötti karaktereket 0..9 közötti számokra tudjuk konvertálni. if (c >= '0' && c <= '9') double x = Convert.ToInt32(c)-'0'; v2.verembe(x); A különben ág akkor következik be, ha egy műveleti jelet találunk. Ilyenkor ki kell vennünk a veremből a két operandust (fordított sorrendben, ez nagyon fontos a nem kommutatív műveletek miatt), és a műveleti jeltől függő műveletet kell végrehajtanunk. Az eredmény a verembe kerül: else double e2 = v2.verembol(); double e1 = v2.verembol(); switch (c) case '+': ert = e1 + e2; break; case '-': ert = e1 - e2; break; case '*': ert = e1 * e2; break; case '/': ert = e1 / e2; break; case '^': ert = Math.Pow(e1,e2); break; v2.verembe(ert); Az utolsó lépés már csak az így kiszámított érték megjelenítése a címkén: ert = v2.verembol(); label1.text = "Érték: " + ert.tostring("#.###"); Ezzel készen vagyunk a lengyel formára hozással és a kiértékeléssel. EGYSZERŰBB FELADAT Egy adott szövegről ellenőrizd a Verem felhasználásával, hogy palindrom-e, azaz odaés visszafelé olvasva ugyanaz (pl. kerek, indulagörögaludni )! 132
133 ÖSSZETETTEBB FELADAT Az alább található rekurzív algoritmust felhasználva a fejezetben megírt gráfos példaprogramot bővítsd ki a mélységi bejárással, amelynek eredményét jelenítsd is meg a másik bejárás mellett! Eljárás MélységiBejárás(pont: Egész, h: Halmaz) h.halmazba(pont) Kiír: pont Ciklus i:=1-tıl pontszám-ig Ha VanÉl(pont, i) és nem h.eleme(i) Akkor MélységiBejárás(i) Ciklus vége. Eljárás vége. Forrás: Szlávi Péter Zsakó László: mikrológia 38 (Módszeres programozás: Gráfok, gráfalgoritmusok) ábra Gráfok szélességi és mélységi bejárása Tipp: a rekurzív eljárás megvalósításakor a Halmaz típusú objektumot ref paraméterként kell átadni, különben hibásan működik a metódus! 133
134 FUTÁSIDEJŰ OBJEKTUMOK Ennek a témakörnek az a célja, hogy megtanuld, miképp kell grafikus objektumokat programkódból, a program futása közben létrehozni. Ez azért fontos, mert így számos egyszerűbb játékprogramot (például memóriajáték, tilitoli) viszonylag könnyen meg tudunk írni, ugyanis sok komponenst tudunk egyszerre kezelni, és valamilyen adatszerkezetbe (tömbbe, mátrixba) tudjuk őket szervezni. ELMÉLETI ÖSSZEFOGLALÓ Eddig minden alkalmazásunk kezelőfelületét tervezési időben készítettük el. Ez azt jelenti, hogy a program indulásakor a Form minden grafikus objektuma rendelkezésre áll. Sokszor azonban szükség lehet arra, hogy a program futása közben, futási időben hozzunk létre komponenseket. Példa: 100 gomb elhelyezése "manuálisan" igen körülményes, és nem lehet egységesen kezelni Példa: címke (Label) objektum létrehozása futási időben Ugyanúgy megy, mint bármilyen más objektum létrehozása Label l = new Label(); Ekkor azonban még nem látszik semmi a Formon Be kell állítani, hogy hol és hogyan jelenjen meg a címke Címke tulajdonságainak beállítása: a legfontosabb a szülő beállítása, azaz hogy melyik objektumon jelenjen meg (általában a Form) l.parent = Form1.ActiveForm; amennyiben ez hiányzik, akkor nem jelenik meg a címke így érjük el az aktív Formot elhelyezkedés a Formon: l.left (távolság a Form bal szélétől) és l.top (távolság a Form tetejétől) a címke szövege (l.text), betűszíne (l.forecolor), Hogyan tudjuk elérni, hogy az így lerakott komponens Click() eseménykezelőjét meg tudjuk írni? Írjunk egy saját metódust, amelynek paraméterezése megegyezik egy Click() eseménykezelő paraméterezésével! Honnan lehet ezt tudni? Tegyünk a Formra egy "fiktív" komponenst, és készíttessük el a fejlesztői környezettel a Click() eseménykezelő vázát, majd másoljuk le a paraméterlistát Példa: private void Klikk(object sender, EventArgs e) Ha megírtuk az eseménykezelőt, akkor simán hozzáadjuk a címke Click() eseményéhez.. l.click += Klikk; De hogyan fogunk tudni ebből az általános eseménykezelő metódusból, amit hozzárendeltünk több címke komponenshez, hivatkozni arra az egy komponensre, amire éppen kattintott a felhasználó? Minden eseménykezelő metódus paraméterlistájában található egy sender nevű paraméter, amely object típusú és azt az objektumot jelenti, aki küldte az eseményt (tehát például azt a címkét, amin kattintottak). 134
135 Az object minden objektum absztrakt (azaz konkrét megvalósítás nélküli) ősosztálya. A küldő objektumra való hivatkozáshoz a megfelelő típussal végzett típuskényszerítésre van szükség. Példa: a küldő (címke típusú) objektum színének megváltoztatása közös eseménykezelőből: private void Klikk(object sender, EventArgs e) (sender as Label).ForeColor = ide írjuk az aktuális objektumtípust Természetesen arra is van lehetőség, hogy ezekből a grafikus objektumokból tömböket, mátrixokat hozzunk létre. Ez most is ugyanúgy történik, mint más objektumtípus esetén. Példa: címkék vektorban Label[] l = new Label[MAX]; Példa: gombok mátrixban Button[,] b = new Button[MAXN, MAXM]; CÍMKÉK ELHELYEZÉSE FUTÁSI IDŐBEN Készítsünk egy programot, amely a képernyő tetszőleges területén kattintva létrehoz egy új címke komponenst, amelynek szövege a kattintás koordinátája (X;Y) alakban! Írjunk hozzájuk közös eseménykezelőt, amely kattintáskor a címke színét véletlenszerűen választott színre változtatja! ábra Dinamikusan létrehozott címkék Készítsünk egy új Windows Forms alkalmazást! A fenti ábrán látható tájékoztató szöveg egy címkén van, amit egy Panel komponensre helyeztünk, ezáltal az a rész nem fog reagálni az egérrel történő kattintásra. 135
136 Mivel a színezéshez szükségünk lesz egy Random objektumra, ezt deklarálnunk kell, és a program indulásakor létre kell hoznunk: Random r; public Form1() InitializeComponent(); r = new Random(); A Formra kattintás lekezelésekor szükségünk lesz a kattintás x és y koordinátájára. A Click() eseménykezelő nem ismeri a kattintás paramétereit, ezért ehelyett a MouseDown() eseménykezelőt kell megírnunk. Annak ugyanis van egy e nevű, MouseEventArgs típusú paramétere, amelynek X és Y tulajdonsága éppen a nekünk kellő koordinátákat tartalmazza. Írjuk meg a Form MouseDown() eseménykezelőjét! Először létrehozzuk a címkét, és a szülő objektumnak beállítjuk az aktív Formot: private void Form1_MouseDown(object sender, MouseEventArgs e) Label l = new Label(); l.parent = Form1.ActiveForm; Az előbbi megjegyzésből már kiderül, hogy az e.x és az e.y felhasználásával be tudjuk állítani a címke pontos helyét. Tegyük hát meg! l.left = e.x; l.top = e.y; A címkék felirata legyen ez a két koordináta, zárójelek között, pontosvesszővel elválasztva: l.text = "(" + e.x.tostring() + ";" + e.y.tostring() + ")"; Végül az így elkészült és elhelyezett címkék Click() eseménykezelőjének állítsuk be az általunk megírásra kerülő Klikk metódust! l.click += Klikk; Készítsük el a Klikk nevű metódusunkat! Paraméterezését már láthattuk az Elméleti összefoglalóban. A címke színének beállításához használjuk a Color.FromArgb() metódust, amelynek egyik lehetséges paraméterezése, hogy megadjuk a kívánt szín R, G, és B összetevőit (itt most mindhárom legyen egy közötti véletlenszám). private void Klikk(object sender, EventArgs e) int R = r.next(0, 256); int G = r.next(0, 256); int B = r.next(0, 256); (sender as Label).ForeColor = Color.FromArgb(R, G, B); Ha mindent jól csináltunk, akkor máris készen van az első, dinamikus grafikus objektumokat tartalmazó programunk. DINAMIKUS CÍMKÉK TÖMBBEN A következő kis mintapélda szintén címkéket hoz létre futási időben, ugyanakkor példát mutat a tömbös használatra, illetve az így létrehozott objektumok megszüntetésére is Készítsünk egy programot, amely az egyik gombra kattintva létrehoz a Formon véletlenszerű helyen véletlen színű címkéket, a másik gomb pedig megszünteti őket! 136
137 20.2. ábra Dinamikusan létrehozott címkék immár tömbben A Form konstruktora előtt deklaráljuk a címkék tömbjét, és a MAX konstanst. A címke tömb létrehozását a konstruktorban tesszük meg. Label[] l; const int MAX = 30; public Form1() InitializeComponent(); l = new Label[MAX]; Fontos! Ez a tömb elemeit, azaz a címke komponenseket nem hozza létre, csak helyet foglal a tömbnek, és innentől megcímezhetők az elemei. A címkéket az előző feladatban látható módon még egyesével létre kell hozni, méghozzá mivel tömbről van szó, ezért ciklusban. A Létrehoz gombra kattintáskor először létrehozzuk az említett ciklusban a címkéket: for (int i = 0; i < MAX; i++) l[i] = new Label(); Szükségünk lesz egy Random objektumra, illetve egy számlálóra, amely a címkékre kerülő számokat tartalmazza. int db = 0; Random r = new Random(); Mivel a tömböt teljesen feltöltöttük, ezért végigmehetünk rajta a rég nem használt foreach ciklussal, hogy beállítsuk a címkék tulajdonságait. foreach(label x in l) Állítsuk be a címkék tulajdonságait: a legfontosabb a szülő, utána a felirat (a számláló egyre növekvő értékei), illetve a szélesség és a magasság: x.parent = Form1.ActiveForm; x.text = (++db).tostring(); x.width = 20; x.height = 20; 137
138 Fontos! Amennyiben átállítjuk a címke szélességét, magasságát, akkor az AutoSize tulajdonságot hamisra kell állítanunk! x.autosize = false; Be kell még állítanunk a háttérszínt (hasonlóan az előző feladat előtérszínéhez, véletlenszerűen), illetve ugyancsak véletlenszámokkal a pozíciót (Left és Top tulajdonság). x.backcolor = Color.FromArgb(r.Next(0, 256), r.next(0, 256), r.next(0, 256)); x.left = r.next(50, Form1.ActiveForm.Width - 50); x.top = r.next(50, Form1.ActiveForm.Height - 50); Célszerű úgy megírni a programot, hogy a Létrehoz gomb megnyomásakor letiltjuk az újabb címkék készítését, egészen addig, amíg a Megszüntet gombot meg nem nyomjuk. button2.enabled = true; button1.enabled = false; Sokkal egyszerűbb a címkék megszüntetése. Ciklusban meghívjuk a Dispose() metódust minden egyes címkére, majd utána engedélyezzük a Létrehoz gombot. for (int i = 0; i < MAX; i++) l[i].dispose(); button1.enabled = true; EGYSZERŰBB FELADAT Készíts programot, amely a Formra kattintáskor létrehoz egy címkét, rajta egy 1 és 100 közötti számmal! Közben számold a Formra kerülő címkék darabszámát, átlagát, illetve írd azt is ki, melyik volt a legkisebb és a legnagyobb érték! ÖSSZETETTEBB FELADAT Készítsünk ötöslottó-sorsolást bemutató programot! Rajzoljunk ki egy 15*6-os táblát címkékből, végezzük el a sorsolást, és jelöljük meg a kihúzott számokat! Használjuk fel a Halmaz osztályunkat! ábra Lottósorsolás dinamikus címkékkel 138
139 SZIMULÁCIÓS PÉLDA: BENZINKÚT Ettől a fejezettől kezdve az eddigi ismereteinket felhasználva készítünk látványos, grafikus programokat. Elsőként egy olyan benzinkút szimulációját készítjük el, amelynél egy sorba érkeznek az autók, és három sorba (95-ös, 98-as, diesel) állhatnak be. Az egyetlen újdonság a fejezetben a Timer (időzítő) komponens használata. SZIMULÁCIÓS FELADAT Készítsük el egy kis benzinkút szimulációját Sor adatszerkezet és futási idejű komponensek felhasználásával! Legyen négy sor: az első, amelybe "beállnak" az újonnan érkező autók, és három másik a három fajtának (95-ös, 98-as és diesel). Szükség van négy időzítőre (Timer), amely az új autók "érkezését" vezérli. A Timer használata: A Timer az egyik legegyszerűbb objektum. Legfontosabb tulajdonságai: Interval: megadja azt az intervallumot (milliszekundumban), amely elteltével automatikusan végrehajtja a Tick() eseménykezelőben leírtakat. Enabled: amikor igazra állítjuk, abban a pillanatban elindul az időzítő. Egyetlen eseménye: Tick: ez az eseménykezelő metódus fut le, amikor az időzítő aktív, és letelik a beállított intervallum. 139
140 Az alkalmazás kezelőfelülete: L e G u B x Nu e cu D w e ke ek utók ábra A szimulációs program kezelőfelülete A könnyebb hivatkozás érdekében megadjuk, hogy az objektumokat hogyan nevezd el (a Name tulajdonságot kell átírni.) Objektum Javasolt név 4 db Sor<Label> (Címke objektummal példányosított Sor) sa, s95, s98, sd (autók, 95, 98, diesel) 4 db Timer ta, t95, t98, td 4 db GroupBox gba, gb95, gb98, gbd 4 db NumericUpDown sha, sh95, sh98, shd (a sorok maximális hosszának megadásához) Működés: Inicializálás: időzítők indítása Sor objektumok és Random objektum létrehozása Autók időzítője: amennyiben van hely a sorban, akkor létre kell hozni új címkét (véletlen színnel), és el kell helyezni a sorban utána meg kell vizsgálni a sor elején álló autó színét, és amennyiben a megfelelő sorban van hely, akkor oda át kell tenni 95-ös, 98-as, diesel időzítők: ha nem üres a sor, akkor a sor elején álló címkét megszüntetjük, a többi címkét pedig előre mozgatjuk ("halad a sor") 140
141 Nagyon fontos, hogy mindegyik időzítőt a Tick() eseménykezelő futása elején le kell állítani, és a végén újra kell indítani! A feladat megoldásához kicsit módosítanunk kell a Sor osztályunkat. Eddig kívülről nem tudtunk a Sor hosszához hozzáférni, ebben a feladatban viszont ez kelleni fog, ezért muszáj írnunk egy csak olvasható tulajdonságot, ami ezt megmondja: public int Hossz get return hossz; Másodszor szükségünk lesz egy olyan műveletre, amely visszaadja a sor elején álló elemet, anélkül, hogy azt kivenné a sorból: public T Elso() if (!UresE()) return se[eleje]; else return default(t); Végül pedig meg kell írnunk egy olyan műveletet is, amellyel el tudjuk érni a sor elemeit. Ez nem része a Sor adatszerkezet specifikációjának, és nem szabadna ilyen műveletet készíteni, de most muszáj, mert amikor kiáll egy autó a sorból, akkor az összes többi elemet előre kell mozgatni. Ehhez megírhatjuk az indexelést, azaz ha s egy Sor, akkor hivatkozhatunk s[i]-re, ami a sor i. eleme. A szintaktika hasonlít a tulajdonságokhoz, csak a fejléc tér el egy picit. A biztonság kedvéért csak olvashatóvá tesszük az indexert: public T this[int i] get if (!UresE()) return se[(eleje + i) % MAX]; else return default(t); A this kulcsszót akkor használjuk, amikor az osztályon belül akarunk hivatkozni saját magunkra. Kezdjük el megírni a programot! Tervezd meg a felhasználói felületét a ábrának megfelelően! A komponenseket nevezd el az előző oldalon található táblázatnak megfelelően! A Form konstruktora elé szokás szerint felvesszük a globális változóinkat, ami ebben az esetben egy számláló (eddig hány címkét tettünk le, azaz hány autó érkezett), a négy Sor objektum, és a Random. int db = 0; Sor<Label> s95; Sor<Label> s98; Sor<Label> sd; Sor<Label> sa; Random r; 141
142 A Form betöltésekor, azaz a Form Load() eseménykezelőjének lefutásakor el kell indítanunk az időzítőket, illetve létre kell hoznunk a Sor és Random objektumokat. ta.enabled = true; t95.enabled = true; t98.enabled = true; td.enabled = true; s95 = new Sor<Label>(); s98 = new Sor<Label>(); sd = new Sor<Label>(); sa = new Sor<Label>(); r = new Random(); Az egyszerűbb feladat megírni 95-ös, 98-as, és a Diesel sorokhoz tartozó időzítők eseménykezelőjét. Mivel a három metódus csak a hivatkozásokban tér el egymástól, ezért csak az egyik kódját adjuk meg. Először is meg kell vizsgálnunk, hogy az adott sor nem üres-e, azaz áll-e benne autó, mert ha nem, akkor onnan nem tudunk elemet kivenni. private void t95_tick(object sender, EventArgs e) if (!s95.urese()) Először is állítsuk le az ide tartozó időzítőt: t95.enabled = false; A következő lépés, hogy a sor elején álló címke objektumot kivesszük a sorból, és megsemmisítjük, azaz meghívjuk a Dispose() metódusát, ezzel eltűnik a Formról. Label l = s95.sorbol(); l.dispose(); Amikor kiállt egy autó a sorból, akkor a többiek előrébb mehetnek: ezért volt szükség az indexer elkészítésére. Menjünk végig a sor objektumain, és a címkéket vigyük 30 pixellel feljebb. for (int i = 0; i < s95.hossz; i++) s95[i].top -= 30; Az utolsó lépésben visszakapcsoljuk a kikapcsolt időzítőt. t95.enabled = true; A t98_tick() és a td_tick() metódusokban ugyanezt kell csinálni, csak a megfelelő sorra és időzítőre kell hivatkozni. Írjuk meg ezek alapján önállóan ezt a két metódust! Lényegesen nehezebb feladat megírni az autók sorának eseménykezelőjét. Először meg kell vizsgálnunk, hogy van-e szabad hely a sorban, azaz érkezhet-e újabb autó. Ha igen, első lépésként máris letiltjuk az időzítőt. private void ta_tick(object sender, EventArgs e) if (sa.hossz < sha.value) ta.enabled = false; Következik az új címke létrehozása. Ne felejtsük el, hogy most szülőnek a GroupBox komponenst kell megadnunk. A címke 30*30 pixeles, és a db változó tárolja a következő sorszámot. 142
143 Label l = new Label(); l.parent = gba; l.autosize = false; l.width = 30; l.height = 30; l.text = (++db).tostring(); A címke színét úgy döntjük el, hogy 1 és 3 között sorsolunk egy véletlenszámot, és annak megfelelően állítjuk be a háttérszínt: switch (r.next(1, 4)) case 1: l.backcolor = Color.Red; break; case 2: l.backcolor = Color.Green; break; case 3: l.backcolor = Color.Blue; break; Majd beállítjuk a címkén levő szöveg igazítását, illetve a pozíciót, amely a sor aktuális hosszától függ. Végül elhelyezzük az elkészült címkét a sorban, és újra engedélyezzük az időzítőt: l.textalign = ContentAlignment.MiddleCenter; l.left = 20 + sa.hossz * 30; l.top = 20; sa.sorba(l); ta.enabled = true; Ennek az elágazásnak nincsen különben ága. Ugyanakkor egy másik elágazásban meg kell vizsgálnunk, hogy a sor nem üres-e, mert ha van benn autó, akkor annak át kell állnia a megfelelő sorba. Az időzítőt természetesen itt is fel kell függesztenünk. if (!sa.urese()) ta.enabled = false; Következik egy háromirányú elágazás. Meg kell vizsgálnunk, hogy a sor elején álló címkének milyen a háttérszíne. Amennyiben piros, akkor meg kell nézni, hogy a piros autók sorában van-e hely egy új autónak (össze kell hasonlítani a sor hosszát a NumericUpDown vezérlőkön beállított maximális hosszal). if (sa.elso().backcolor == Color.Red) if (s95.hossz < sh95.value) Ha ezek a feltételek teljesülnek, akkor a piros sorból átáll egy autó a 95-ös sorba. Ezért a 95-ös sorhoz tartozó időzítőt le kell állítani és a címkét a megfelelő sorból ki kell venni. t95.enabled = false; Label l = sa.sorbol(); Egy címkét úgy tudunk áthelyezni, hogy megváltoztatjuk a Parent tulajdonságát. Az imént kivett címkét tehát ilyen módon áttesszük a másik GroupBox-ra, beállítjuk a pozícióját, és elhelyezzük a 95-ösök sorában. l.parent = gb95; l.left = 20; l.top = 20 + s95.hossz * 30; s95.sorba(l); 143
144 Az autók sorában a címkéket el kell tolni balra, hiszen egy autó kiállt a sorból. Végül újra engedélyezzük a 95-ös időzítőt. for (int i = 0; i < sa.hossz; i++) sa[i].left -= 30; t95.enabled = true; Következik a másik színhez tartozó elágazás else if (sa.elso().backcolor == Color.Green) és ugyanígy a harmadik: else if (sa.elso().backcolor == Color.Blue) Végül az alapsor időzítőjét is vissza kell kapcsolni. ta.enabled = true; A pontozott helyekre az előzőek alapján írd meg a kódot! Csak a megfelelő objektumokra való hivatkozást kell használnod Ezzel elkészült a benzinkút-szimulációs programunk. 144
145 EGYSZERŰBB FELADAT A benzinkutunkhoz megérkezett az új, E85-ös környezetkímélő üzemanyag. Bővítsd ki a benzinkutas programot, hogy a negyedik üzemanyagfajtát is tudja kezelni! Lehessen továbbá állítani az egyes sorokhoz tartozó időzítő hosszát is! ábra Benzinkút szimuláció kiegészítve 145
146 JÁTÉK KÉSZÍTÉSE: MEMÓRIA Ebben a fejezetben egy memóriajátékot készítünk futási időben létrehozott gombok segítségével, lényegében az eddigi ismereteink felhasználásával. MEMÓRIAJÁTÉK KÉSZÍTÉSE Készíts játékprogramot, amely véletlenszerűen elrejt számpárokat, és a játékos feladata, hogy ezeket a párokat felfedjük! ábra Memóriajáték Helyezzünk el a Formon 16 gombot futási időben. Egy gombra kattintáskor megmutatjuk az "alatta lévő" számot. A következő kattintáskor megmutatjuk a másik számot is. Amennyiben a két szám egyezik, a gombok színt váltanak, a párok felfedve maradnak. Amennyiben viszont két különböző számot találtunk, 2 másodperc elteltével a számok eltűnnek, és újra lehet próbálkozni. Segítség a program megírásához: Természetesen szükségünk lesz egy Button objektumokat tartalmazó tömbre. Az, hogy ez most egy 4*4-es mátrix, vagy egy 16 elemű vektor, a programozóra bízzuk. Az "Új játék" gombra kattintás után hozzuk létre a gombokat. Figyeljünk arra, hogy mi történik, ha a felhasználó már létező gombok mellett kattint az "Új játék"-ra! Célszerű megszüntetni előtte a gombokat, amennyiben már léteznek. A párokat véletlenszámok segítségével "osztjuk ki" a gombok között. A gombokhoz készítsünk közös eseménykezelőt! A gombon levő szám elrejtését megoldhatjuk úgy, hogy azonos háttér- és betűszínt állítunk be, de ez itt most nem ad annyira szép eredményt. Vagy: használhatjuk a Button objektum Tag tulajdonságát. Ez object típusú, de konvertálással tetszőleges értéket tárolhatunk benne. 146
147 Tag tulajdonság értékének átalakítása egész számmá: (int)button1.tag szöveggé: button1.tag.tostring() Nyilván kell tartani, hogy volt-e előzőleg megnyomott gomb. Ehhez hozzunk létre egy Button típusú lokális változót, amelynek az értéke null, ha nincsen hozzárendelve semmilyen objektum. Így ellenőrizhető, ha még nem rendeltünk hozzá gombot, azaz még nem fordítottuk fel a pár első tagját. Kattintás címzettjének hozzárendelése: Button b = sender as Button; Várakozás 2 másodpercig (2000 ms): Application.DoEvents(); System.Threading.Thread.Sleep(2000); Továbbfejlesztési lehetőségek: Választható táblaméret (persze ésszerű határokon belül) Képek a számok helyett Legjobb eredmények (legkevesebb lépésből kirakás) tárolása, fájlba mentése (TOP 10) Többjátékos mód 147
148 JÁTÉK KÉSZÍTÉSE: TILITOLI Ebben a fejezetben egy tilitoli játékot készítünk futási időben létrehozott gombok segítségével, az eddigi ismereteink, és az előző fejezetben elkészített memóriajáték tapasztalatainak felhasználásával. TILITOLI JÁTÉK KÉSZÍTÉSE 23.1.Írd meg a tilitoli nevezetű játékprogramot! ábra Tilitoli Egy 4*4-es mátrixban tegyünk le 16 gombot úgy, hogy a gombokon véletlenszerűen helyezzük el a számokat 1-től és 15-ig, a 16. helyen (jobb alsó sarokban) lévő gomb maradjon üresen. Amennyiben az üres hely valamelyik szomszédjára kattintunk, akkor a gomb "becsúszik" az üres helyre. Rendezzük át úgy a gombokat, hogy a rajtuk lévő számok sorfolytonosan növekvő sorozatot adjanak ki! Segítség a program megírásához: Természetesen most is szükségünk lesz egy Button objektumokat tartalmazó tömbre. Ebben az esetben célszerű egy 4*4-es mátrixot használni. A feltöltéskor a 16. gombot másképpen kell színezni, és az Enabled tulajdonságát is hamisra kell állítani. Az "Új játék" gombra kattintás után hozzuk létre a gombokat. Figyeljünk arra, hogy mi történik, ha a felhasználó már létező gombok mellett kattint az "Új játék"-ra! Célszerű megszüntetni előtte a gombokat, amennyiben már léteznek. A gombokhoz most is készítsünk közös eseménykezelőt! Ebben az eseménykezelőben meg kell vizsgálni, hogy egyáltalán engedélyezett helyre kattintott-e a felhasználó (azaz az üres hely valamelyik szomszédjára). Ehhez használhatjuk az előző fejezetben már említett Tag tulajdonságot. 148
149 Tag tulajdonságok feltöltése: m[i,j].tag = i*10+j Abban az esetben, ha az üres hely Tag értéke (kezdetben 33) és a kattintott gomb sorszáma közti különbség 1 vagy 10 (tehát mellette vagy alatta-felette van), akkor érvényes a lépés! Érvényes lépés esetén a megnyomott gomb és az "üres" gomb feliratot, színt és tulajdonságokat cserél, és a továbbiakban ez a gomb lesz az "üres". Minden érvényes lépés után ellenőrizzük, hogy sikerült-e kirakni a tilitolit: a bal felső sarokból elindulva sorfolytonosan megnézzük a címkék feliratait, és ha egyesével növekvő sorozatot kapunk, akkor a megoldás helyes. Továbbfejlesztési lehetőségek: Választható táblaméret (persze ésszerű határokon belül) Képek a számok helyett Legjobb eredmények (legkevesebb lépésből kirakás) tárolása, fájlba mentése (TOP 10) Sajnos ez a verzió nem garantálja, hogy a véletlenszerű keverés kirakható táblát eredményez, ezért meggondolandó, hogyan lehet olyan keverési algoritmust írni, amellyel garantáltan kirakható kezdőállapothoz jutunk. 149
150 SZÁMONKÉRÉS: PROGRAM ÍRÁSA Ebben a fejezetben egy kitűzött feladatot kell jegyre megoldani, az eddigi ismeretek felhasználásával Írd meg a lottósorsolást szimuláló programot! Tegyél ki futási időben 90 db címkét, amelyek közül pontosan öt darabot lehet megjelölni, rákattintással. Amennyiben olyan címkére kattintasz, amit már megjelöltél, akkor az elveszti a jelöltségét. Amikor megvan az öt bejelölt címke, sorsolj ki öt különböző számot, majd hasonlítsd össze a bejelöltekkel. Számold meg, hány számot sikerült eltalálni! A feladat megoldásához a már elkészített Halmaz osztályt fel lehet használni ábra Lottósorsolás kiegészítve 150
151 36 FELADAT A C# JEGYZETHEZ KÉSZÍTETTE: CZIGLÉCZKY GÁBOR
152
153 WINDOWS FORMS ALKALMAZÁS KÉSZÍTÉSE FELADATOK EGYSZERŰBB FELADATOK 1.1 Készíts programot, amely egy, a rajta levő szövegnél nagyobb méretű címkén képes a szöveget mind a kilencféleképpen igazítani! (01_Igazitas) Megjegyzés: ahhoz, hogy a címke átméretezhető legyen, az AutoSize tulajdonság értékét hamisra kell állítani! 1.2 Készíts programot, amely egy címkére kattintva mindkét irányban 10 pixellel növeli, egy másikra kattintva pedig csökkenti a Form méretét! (01_NovelCsokkent) ÖSSZETETTEBB FELADAT 1.3 Készíts programot, amely a Formot mozgatja a rajta lévő iránygombokkal! (01_Mozgat, a gombokhoz szükséges képfájlok mellékelve.) 35. ábra Ablak mozgatása nyílgombokkal Tippek a megoldáshoz: - A képek tárolásához adjunk az alkalmazáshoz egy ImageList objektumot. - Ellenőrizzük, hogy az ImageSize tulajdonság értéke 16; 16, azaz 16*16 pixeles legyen egy kép mérete! - Az Images tulajdonságnál nyomjuk meg a feliratú gombot, majd a megjelenő párbeszédablakban az Add nyomógombbal adjuk hozzá a gyűjteményhez a 4 db képfájlt. - A TransparentColor tulajdonságot állítsuk a Web fülön található Olive színűre! Ezzel elkészült a képlistánk. - Minden gombon válasszuk ki az ImageList tulajdonságban a képlistánkat, majd az ImageIndex jellemzőnél választhatunk a képek közül. 153
154 ADATOK BEOLVASÁSA ÉS KIÍRÁSA, ALAPVETŐ ADATTÍPUSOK, TÍPUSKOVERZIÓK, MATEMATIKAI ÉS LOGIKAI MŰVELETEK FELADATOK EGYSZERŰBB FELADATOK 2.1.Készíts programot, amely egy címkét tartalmaz (rajta egy számmal, amelynek a kezdőértéke 0). A címkére kattintáskor növelje meg a számot, és a megnövelt értéket írja ki! (02_Szamol) 2.2.A táplálkozási szakemberek a BMI indexet (Body Mass Index) tartják a legmegbízhatóbb számításnak az elhízás mértékének meghatározásához. Segítsünk nekik! Készíts programot, amely beolvassa egy illető testmagasságát (centiméterben), illetve a testsúlyát (kilogrammban), majd kiszámítja a BMI-t és kiírja, két tizedesre kerekítve! (02_BMI) ú = á (ahol a magasság a méterben vett testmagasság!) Megjegyzés: két tizedesre kerekíteni a Math.Round függvénnyel lehet. ÖSSZETETTEBB FELADAT 2.3.A tanulói jegyzet 3.7-es feladata a következő volt: írj programot, amely bekéri egy derékszögű háromszög két befogójának hosszát, és ebből kiszámolja az átfogó hosszát (Pitagorasz-tétellel)! Az eredményt címkén, jelenítsd meg! Készítsd el az alapfeladat megoldását, ha még nem tetted meg! Ezt követően egészítsd ki azzal, hogy számolja ki az így kapott derékszögű háromszög két szögét! (02_Pitagorasz_Szogek) Emlékeztető: Derékszögű háromszögben: si = (Amennyiben az a oldal az α szöggel szembeni befogó, a c pedig az átfogó. Átváltás radiánból szögbe: 36 2 A szinusz függvény inverzét, az arkusz szinuszt a Math.Asin függvénnyel érhetjük el. 154
155 VEZÉRLÉSI SZERKEZETEK: ELÁGAZÁSOK, CIKLUSOK FELADATOK EGYSZERŰBB FELADATOK 3.1.Készíts programot, amely egy telefonszámról eldönti, hogy melyik hazai mobilszolgáltatóhoz tartozik! A mobilszám +36 -tal és 06 -tal is kezdődhet. Ellenőrizni kell továbbá azt is, hogy pontosan annyi számjegyet adott-e meg a felhasználó, amennyit kell. A Form háttere változzon a mobilszolgáltatónak megfelelő színűre! (03_Szolgaltato) 3.2.Készíts programot, amely beolvassa egy hónap sorszámát, és megmondja, hogy az adott hónap milyen évszakhoz tartozik! 3.3.Készíts programot, amely kiírja a képernyőre az első n db négyzetszámot (ahol n értékét a felhasználó állíthatja be)! 3.4.Készíts programot, amely a Formot pattogtatja a képernyőn! Amikor eléri a képernyő valamelyik szélét, pattanjon vissza, és az ellenkező irányba folytassa a mozgást! 3.5.Készíts programot, amely kiírja a képernyőre egy adott szám osztóit! 3.6.Készíts programot, amely beolvassa egy sorozat első elemét, illetve a felhasználó választását, hogy számtani vagy mértani sorozatot szeretne-e. A választásnak megfelelően olvassa be a differenciát vagy a kvócienst, és írja ki a sorozat első n darab elemét, ahol az n értékét szintén beolvasással lehessen meghatározni! ÖSSZETETTEBB FELADAT 3.7.Készíts programot, amely egy, a felhasználó által megadott intervallum elemei közül kiválogatja és kiírja a prímszámokat! Ügyelj arra, hogy a felhasználó csak érvényes intervallumot adhasson meg! (Alsó határ >=2, és alsó határ <= felső határ.) Prím eldöntés algoritmusa: Változók: i : Egész (ciklusváltozó) x : Egész (a szám, amelyről el kell döntenünk, hogy prím-e) prime : Logikai (prím-e az x?) Ha x=1 Akkor prime := hamis Különben i := 2; prime := igaz Ciklus amíg (i <= négyzetgyök(x)) és (prime) Ha x mod i = 0 akkor prime := hamis különben i := i+1 Elágazás vége. Ciklus vége. Elágazás vége. 155
156 ÖSSZETETT ADATTÍPUSOK: REKORD, TÖMB, FELTÖLTÉS SZÖVEGFÁJLBÓL FELADATOK EGYSZERŰBB FELADATOK 4.1.Készíts programot, amely a felhasználó által megadott méretű tömböt feltölt (szintén a felhasználó által beírt intervallumon belüli) véletlen számokkal! Jelenítsd meg ezeket egy ListBoxban! 4.2.Az előző pontban elkészített programot egészítsd ki! Egy gomb megnyomására válogassuk ki a prímszámokat egy másik tömbbe (hasonló módon jelenítsük is meg őket), felhasználva az előző fejezet végén szereplő algoritmust! A kiválogatott elemeket írjuk ki egy szövegfájlba! ÖSSZETETTEBB FELADAT 4.3.Készíts programot, amely egy versenyen indulók nevét és pontszámaikat dolgozza fel! A mellékelt verseny.txt tartalmazza a páratlan sorokban a versenyző nevét, a páros sorokban pedig a versenyen elért pontszámot. Olvasd be ezeket az adatokat egy rekordból álló tömbbe, és jelenítsd meg például a következő formában: Kovács Géza (17 pont) Válogasd ki egy másik tömbbe azokat, akik bejutottak a verseny díjazottjai közé! Ehhez az kell, hogy a felhasználó által megadott (40 és 80 közötti) pontszámnál a versenyző többet érjen el! A díjazottak listáját jelenítsd meg a képernyőn, és a fenti formátumot megtartva egy szövegfájlban is! 156
157 ALAPVETŐ PROGRAMOZÁSI TÉTELEK MEGVALÓSÍTÁSA FELADATOK EGYSZERŰBB FELADAT 5.1.Egészítsd ki a 4.3-as pontban elkészített programot az alábbi feladatok megoldásával! (05_Verseny) Add meg, hogy átlagosan hány pontot értek el a tanulók a versenyen (két tizedesre kerekítve)! Add meg, hogy mennyi volt a legtöbb és a legkevesebb pontot elérő tanuló pontszáma és neve! Számold meg, hány olyan versenyző volt, aki egy adott pontszámnál többet ért el! Aki 39 pontot ért csak el, annak 1 pont hiányzott a továbbjutáshoz. Volt-e ilyen tanuló, és ha igen, ki volt az? (Ha volt ilyen, elég az elsőt megadni.) 36. ábra A Verseny feladat megoldása ÖSSZETETTEBB FELADAT 5.2.Készíts programot, amely kockadobást szimulál (szabályos hatoldalú kockával). Tölts fel egy 1000 elemű tömböt 1 és 6 közötti véletlen számokkal. Számold meg (ügyes algoritmussal, lehetőleg elágazás használata nélkül), hogy melyik dobott szám hányszor fordult elő! Az eredményt jelenítsd meg grafikusan (mondjuk színes címkékkel, amelyeknek hossza legyen arányos az előfordulások számával)! 157
158 ÖSSZETETT PROGRAMOZÁSI TÉTELEK MEGVALÓSÍTÁSA FELADATOK EGYSZERŰBB FELADAT 6.1.Egy szövegfájlban (konyvek.txt) adottak a könyvekről a következő adatok (egy sorban): ára, raktári darabszáma, címe. Tölts fel egy tömböt az adatokkal! Add meg, mekkora a raktárkészlet összértéke! Van-e olyan könyv, amiből rendelni kell, mert kifogyott a raktárból? Hány fajta könyvet forgalmaz a bolt egy adott ár alatt? Egészítsd ki kivételkezeléssel, hogy hibát jelezzen, ha az adott mező üres vagy nem megfelelő formátumú a beírt adat! Mennyi a legdrágább könyv ára? Megjegyzés: A beolvasás itt bonyolultabb, mint a szokásos esetben, ugyanis egy sor több szóközt tartalmaz, mint ahány darabra kell vágnunk a sort (mivel a könyv címe is tartalmazhat szóközt). Emiatt a Split függvényt itt úgy kell paramétereznünk, hogy mindenképpen három darabra szedje szét a beolvasott szöveget. Ezt azonban csak úgy tehetjük meg, ha az első paraméter nem egyetlen karakter, hanem karaktertömb, amelynek most csak egy paramétere van: string [] darabolt = sor.split(new char[] ' ',3); 37. ábra A Könyves feladat egy lehetséges eredménye 158
159 ÖSSZETETTEBB FELADATOK 6.2.Egészítsd ki a 6.1. feladatban megírt programot a következő feladatok megoldásával: (06_Konyves) Rendezd az adatokat a könyv címe szerint! Mennyibe kerül a kedvenc könyved? Olvass be a felhasználótól egy címet, és logaritmikus kereséssel keresd meg az adott könyvet és írd ki az árát! Válogasd ki a megadott ár feletti könyveket! Használj metódusokat, hogy ne kelljen még egyszer megírnod például a kiíratást! Írd át a megjelenítést olyanra, hogy paraméterezhető legyen, melyik DataGridbe ír ki! 6.3.Egy szövegfájlban adottak madárfajokról a következő adatok (két külön sorban): neve, élőhelyek száma. Tölts fel egy vektort az adatokkal! Jelenítsd meg a képernyőn tetszőleges vizuális eszköz használatával! (A feladat további része szempontjából célszerű külön Kiír metódust készíteni.) Melyik madárfajnak van a legkevesebb élőhelye? Írd ki a madárfaj nevét! Rendezd az adatokat madárfaj neve szerint csökkenően, illetve élőhelyek száma szerint növekvően! Olvasd be a felhasználótól egy madárfaj nevét, és logaritmikus kereséssel keresd meg az adott madarat és írd ki az élőhelyeinek számát! Csak akkor engedélyezd a logaritmikus keresés elindítását, ha az adathalmaz név szerint rendezett! 38. ábra A "madaras" feladat megoldása 159
160 KIVÉTELKEZELÉS FELADATOK EGYSZERŰBB FELADAT 7.1.Írjuk át a tanulói jegyzet 8.3-as feladatát, úgy, hogy a hibalehetőségeket lehetőség szerint saját kivételosztály létrehozásával kezeljük le! Minden hibára külön üzenettel reagáljunk! A feladat: Írjunk programot, amely bekéri a másodfokú egyenlet a, b, c együtthatóit, és meghatározza az egyenlet gyökét vagy gyökeit, amennyiben ez lehetséges! A programban kezeljünk le minden lehetséges esetet (a=0, diszkrimináns negatív, nulla, illetve pozitív) is! Saját kivételosztály létrehozása: A Form1 osztály után kell felvennünk a saját kivételosztályunkat: class SajatKivetel : SystemException public SajatKivetel(string uzenet) : base(uzenet) A saját kivételosztályt a SystemException osztályból kell származtatnunk. Egyetlen metódust, a konstruktort fogjuk felüldefiniálni, mégpedig azt, amelyik a kivétel hibaüzenetét tartalmazza. Csak annyit kell tennünk, hogy meg kell hívnunk az ősosztály konstruktorát, és át kell passzolnunk a hibaüzenetet neki. A saját kivétel eldobása (például a=0 esetén): if (a == 0) throw new SajatKivetel("Nem másodfokú az egyenlet!"); A saját kivétel elkapása: catch (SajatKivetel ex) MessageBox.Show(ex.Message, "Hiba", MessageBoxButtons.OK, MessageBoxIcon.Error); Itt mindenképpen adnunk kell neki egy változót (a példában ex), amin keresztül az üzenetet fogadni tudjuk. Ezt íratjuk ki a MessageBox függvénnyel, amelyet kicsit több paraméterrel hívtuk meg, annak érdekében, hogy a hiba ikont el tudjuk helyezni az üzenetablakon. 39. ábra Saját hibaüzenet kiíratása 160
161 KONZOL ALKALMAZÁS KÉSZÍTÉSE FELADATOK EGYSZERŰBB FELADATOK 8.1.Készíts konzol módban futó programot, amely egy 8 elemű tömbbe véletlenszerűen 0-t vagy 1-est tesz! Az így kapott bináris számnak számoljuk ki a 10-es számrendszerbeli megfelelőjét! (08_Binaris_Decimalis) 8.2.A 8.1. feladatban elkészített programot egészítsd ki azzal, hogy a bináris szám hexadecimális (azaz 16-os számrendszerbeli) alakját is adja meg! ÖSSZETETTEBB FELADATOK 8.3.Írj menüvezérelt programot, amely beolvas egy karakterláncot, majd elvégzi a következő feladatokat: (08_Karakterlancok) Kiírja a szöveg hosszát Kiírja a szöveget fordítva Megszámolja, hány szóköz van a szövegben Az első A" betűtől kiírja a szöveg következő 10 karakterét Kiveszi a szövegből a szóközöket Beolvas két karakterláncot (amit, és amire ki akarunk cserélni a szövegben), és elvégzi a cserét Beolvas egy szövegrészt, és azt kitörli a szóból Beolvas egy szövegrészt, meg egy indexet és beszúrja a szövegrészt a szóba a megadott helyre. Szövegkezelő függvények: Legyen s egy string típusú változó. - Szöveg hossza: int hossz = s.length; - Hivatkozás a szöveg i. karakterére: s[i]. - Keresés a szövegben: int poz = s.indexof(keresett); Ha a visszaadott érték -1, akkor nincs a keresett karakter vagy szöveg a sorozatban, különben pedig az első előfordulás kezdő pozícióját adja meg. - Szöveg egy részének kimásolása: string s2 = s.substring(kezdet, db); A szövegből a kezdet sorszámú karaktertől kezdve kimásol db darab karaktert, ha van annyi. Ha nincs, hibát jelez. Ha db-t elhagyjuk, akkor a szöveg végéig másol. - A szövegben egy rész minden előfordulásának cseréje másik szövegrészre: string s2 = s.replace(mit, mire); - Szövegrész törlése: string s2 = s.remove(kezdet, db); A szövegből a kezdet sorszámú karaktertől kezdve kitöröl db darab karaktert, ha van annyi. Ha nincs, akkor hibát jelez. - Szövegrész beszúrása: string s2 = s.insert(kezdet, s1); A megadott pozíciótól kezdve beszúrja a szövegbe az s1 tartalmát. 161
162 8.4.Rendelkezésünkre áll egy labdarúgócsapat mérkőzéseinek kimenetele. Az idén 30 meccset játszott a csapat. A rugott.txt állományban tároljuk, hogy hány gólt rúgott mérkőzésenként, a kapott.txt állományban pedig azt, hogy hány gólt kapott mérkőzésenként. Minden sorban csak 1 adat található. Olvasd be és tárold el a meccsen rúgott és kapott gólokat, majd jelenítsd meg a képernyőn a következő formátumban: 3 1 (ahol 3 a rúgott góljaiknak száma, 1 a kapott góloké.) Írd ki az előző formátumban az eredményeket az eredmeny.txt fájlba. Minden sor egy meccs eredményét tartalmazza. Számítsd ki, hogy hány ponttal zárta a csapat a bajnokságot, ha a győzelemért 3 pont, a döntetlenért 1 pont jár, a vereségért nem jár pont! Add meg, hogy hány olyan mérkőzés volt, ahol nem rúgott gólt a csapat! Add meg, hogy hány mérkőzést nyert meg idén a csapat! Írd ki annak a meccsnek az eredményét, amikor a legnagyobb volt a különbség a rúgott és a kapott gólok között! 162
163 MÁTRIXOK FELADATOK EGYSZERŰBB FELADATOK 9.1.Olvassunk be egy 3*3-as mátrixot (fájlból vagy billentyűzetről), majd ellenőrizzük, hogy a mátrix bűvös négyzet-e, azaz sorainak, oszlopainak és átlóinak összege azonos-e! 9.2.Egy téglalap alakú területre véletlenszerűen dobálunk korongokat. Akkor fejezzük be, ha teljesen befedik a területet. Szimuláld ezt a képernyőn, feltételezve, hogy csak egész koordinátájú pontokra eshet korong és egymás tetején több is lehet. Az egyes mezőkön lévő korongok számát jelenítsd meg egy DataGridben! Határozd meg, melyik pozícióra került a legtöbb korong, és hány olyan pozíció van, amelyen egyetlen korong található! 9.3.Generálj egy N*M-es mátrixot! N és M értékét a felhasználó adhatja meg. Töltsd fel a mátrix elemeit véletlenszerűen megadott osztályzatokkal (1-5-ig)! Jelenítsd meg az adatokat DataGridView segítségével! (09_Osztalyzatok) Az oszlopokban az egyes tantárgyak jegyei találhatók. Számolj átlagot tárgyanként! Hányadik oszlopban (melyik tantárgyhoz) a legmagasabb az átlag? A sorok képviselik a diákok osztályzatait: számolj átlagot diákonként! Vedd figyelembe, hogy aki valamilyen tantárgyból bukik, annak az átlaga 1. Melyik diák bukott a legtöbb tárgyból? (Melyik sorban van a legtöbb 1-es?) ÖSSZETETTEBB FELADATOK 9.4.Egy közös költség befizetési táblázatot kell feldolgoznod mátrix adatszerkezet használatával. Két fájl áll rendelkezésedre: a nevek.txt, mely az első sorában tartalmazza a lakók számát, majd a házban lakók neveit, illetve a befizetesek.txt, mely soronként tartalmazza az egyes lakók befizetéseit havonkénti bontásban ;-vel elválasztva. Olvasd be az adatokat egy vektorba és egy mátrixba, majd jelenítsd meg a táblázatosan a képernyőn úgy, hogy a sorok fejléce legyen a lakók neve, és az oszlop fejlécében a hónapok sorszáma szerepeljen! Add meg minden lakó ez évi befizetéseinek összegét a táblázat végén lévő oszlopban! Add meg, hogy ki, és melyik hónapban fizetett be az év folyamán egyszerre a legtöbbet! Vizsgáld meg az adatokat, hogy volt-e olyan, aki valamely hónapban nem fizetett be! Ha van ilyen, akkor írasd ki a képernyőre, hogy ki volt az, és hányadik hónapban! 163
164 9.5.A rejtveny1.txt állományban egy kitöltött rejtvény adatait találja. Az X karakter helyettesíti a rejtvénytáblázat szavakat elválasztó mezőit (ide nem kerülhet betű, és egymás mellett nem lehet két elválasztó). Az állomány első sorában a rejtvénytáblázat sorainak (N 50) és (M 50) száma található egymástól szóközzel elválasztva. A következő N sorban M darab karakter található. Készítsen programot, melyben megoldja a következő feladatokat! (09_Rejtveny) Olvassa be a mellékelt állományt és írja ki a képernyőre (DataGridViewba) megfelelő sorokra tördelve a keresztrejtvényt oly módon, hogy az X betű helyett üres maradjon a mező! Figyeljen arra, hogy a sorok fejlécei az angol ábécé betűit, az oszlopok fejlécei pedig a sorszámokat tartalmazzák! Állapítsa meg és írja a képernyőre, hogy hány üres mező van a rejtvényben! (Ez azt jelenti, hogy hány X karakter van a mátrixban.) A felhasználó megad egy betűt. Van-e a keresztrejtvényben ez a betű? Ha igen, melyik sorban, illetve oszlopban? Számoljuk meg, hogy az angol ábécé betűi hányszor szerepelnek a rejtvényben! A választ tömbbel adja meg, amit jelenítsünk is meg a képernyőn (egy másik DataGrid segítségével)! Az előző feladat megoldását felhasználva adjuk meg, hogy az angol ábécé melyik magánhangzójából van a legkevesebb! Melyik sorban van a legtöbb a megadott betűből? A sor számát írja a képernyőre! 40. ábra A Rejtvény feladat megoldása 164
165 EMELT SZINTŰ ÉRETTSÉGI JELLEGŰ FELADAT ÓRAREND 10.1.Egy iskolában legfeljebb 40 óra van hetente, hétfőtől péntekig minden nap legfeljebb 8 óra. Az iskolában maximum 20 különböző nevű tantárgyat tanítanak. Az ora.txt állományban rögzítettük egy hét órarendjét. A szövegfájlban pontosan 40 sor található. Minden sorban egy tantárgy neve található, vagy egy * karakter, ahol nincsen óra. Lyukas órának nevezünk egy órát, ha nap közben nincs óra, és előtte meg utána is van óra. Példa egy nap órarendjére: * fizika matematika testnevelés ének rajz angol * A fenti példában az osztálynak nincsen 1. és 8. órája, tehát aznap 6 órájuk van, a óra között. Készítsen orarend néven programot, melyben megoldja a következő feladatokat! Jól különítse el a részfeladatokat (például nyomógombok használatával)! (10_Orarend_WinForms, illetve 10_Orarend_Konzol) Olvassa be az ora.txt állományban talált adatokat egy megfelelő adatszerkezetű változóba, és annak felhasználásával oldja meg a következő feladatokat! Jelenítse meg az órarendet áttekinthető, táblázatszerű formában! Írja ki a képernyőre, hetente hány órája van az osztálynak! Hány dupla óra van a héten? A darabszámot írja a képernyőre! (Akkor beszélünk dupla óráról, amikor két egymást követő óra tantárgyneve teljesen azonos. Kettőnél több azonos óra nincsen egymás után.) Van-e az osztálynak lyukas órája a héten? Ha igen, melyik nap, melyik órájában? A nap nevét és az óra számát (pl.: Csütörtök 7. óra) írja ki a képernyőre! Készítsen statisztikát arról, hogy melyik tantárgyat hány órában tanulja az osztály! A kimutatásban szerepeljenek a különböző nevű tantárgyak és az óraszám! Például: irodalom 2 nyelvtan 2 matematika 5 Rendezze az előző feladatban kapott adatokat heti óraszám, azon belül pedig tantárgynév szerint, növekvő sorrendben! A rendezett adathalmazt is jelenítse meg a képernyőn! A rendezett adathalmaz segítségével írja ki a képernyőre az(oka)t a tantárgya(ka)t, amelye(ke)t a legalacsonyabb óraszámban tanulnak a diákok! Például: A legkevesebb óra kémia, matematika fakt, rajz, ének tárgyból van. 165
166 41. ábra Az Órarend feladat egy lehetséges megoldása (Windows Forms) 42. ábra Az Órarend feladat egy lehetséges megoldása (Konzol) 166
167 FUTÁSI IDŐBEN LÉTREHOZOTT OBJEKTUMOK FELADATOK EGYSZERŰBB FELADATOK 11.1.Generálj futási időben egy 5*5-ös táblát gombokból, az ábrán látható módon, 1-től 25- ig számozva! (11_Nyomogombok) A gombokra kattintáskor folyamatosan számolja és jelenítse meg az addig lenyomott gombok összegét! Készíts statisztikát, hogy melyik gombot hány alkalommal nyomták meg! Ezt minden gombnyomáskor frissítsd, és jelenítsd meg áttekinthető formában (pl. DataGridView)! 43. ábra Nyomógombok dinamikus létrehozása 167
168 11.2.Készíts szorzótáblát gyakoroltató programot! Helyezz el futási időben 10*10 db TextBoxot! Írd meg úgy, hogy a szorzat beírása után az Enter billentyűt kelljen lenyomni az ellenőrzéshez! Közben számold, összesen hány próbálkozása volt a felhasználónak, és ebből mennyi volt helyes! A program álljon meg, ha mind a 100 számot kitaláltuk! (11_Szorzotabla) 44. ábra A szorzótáblát gyakoroltató program Tippek a megoldáshoz: - A billentyűlenyomások vizsgálatához a TextBox KeyDown eseményét érdemes megírni. - Az Enter billentyű lenyomásának vizsgálata: if (e.keycode == Keys.Enter) - A szorzat helyes értékét érdemes a Tag tulajdonságban eltárolni. - A következő hely sorsolásakor érdemes a fehér háttérrel rendelkező TextBoxok közül választani, ezáltal könnyen elkerülhető, hogy olyan mezőt sorsoljunk ki, ami már egyszer volt. - A szövegmező betűtípusának félkövérré tétele: textbox1.font = new Font(textBox1.Font, FontStyle.Bold); 168
169 ÖSSZETETTEBB FELADATOK 11.3.Készíts programot, amely gondol egy számra, majd a felhasználónak segít kitalálni a gondolt számot! (11_Gondoltam) A megvalósításhoz készíts több formos alkalmazást, amely az egyes ablakokat futási időben hozza létre! Legyen egy fő form (FoForm), amelyről be lehet állítani a kitalálandó szám intervallumát, illetve tartalmazza magát a játékot! Legyen egy második form (MinMax), amelyen a minimum és maximum értéket lehet beállítani! Legyen egy harmadik form (Inputbox), amellyel meg tudjuk valósítani a korábban Visual Basic-ből átvett adatbevitelre szolgáló ablakot. A program számolja a tippek számát, és a végén írja ki, hány lépésben sikerült kitalálni a számot! Tippek a megoldáshoz: FoForm: - Ahhoz, hogy kilépéskor rá tudjunk kérdezni arra, hogy valóban ki szeretnénk-e lépni, a form FormClosing eseményét kell kezelnünk. Amennyiben meg akarjuk akadályozni a form bezárását, a következő utasítást kell kiadnunk, ebben az eseménykezelőben: e.cancel = true; - Vegyünk fel két tagváltozót (min és max), amellyel el tudjuk tárolni a másik formon beállított értékeket! - Ezt a két változót kell átadnunk a MinMax formnak, a következőképpen: MinMax mm = new MinMax(min, max); - Ehhez természetesen módosítanunk kell a MinMax form konstruktorát is. MinMax: - A form konstruktorát ki kell bővítenünk két paraméterrel, amelynek az értékét átvesszük és értékül adjuk a form két saját tagváltozójának (Min és Max). public MinMax(int min, int max) InitializeComponent(); Min = min; Max = max; - A form bezárásakor érdemes megvizsgálni, hogy a Min < Max feltétel teljesül-e, illetve kivételkezeléssel biztosítani, hogy ne lehessen hibás értéket megadni. Amennyiben a beírt értékek nem megfelelők, az ablak bezárását meg kell akadályozni. Inputbox: - A formon elhelyezett OK gomb DialogResult tulajdonságát állítsuk OK-ra, a Mégsem gombét pedig Cancel-re! - A form AcceptButton tulajdonságánál válasszuk ki az OK gombunkat, a CancelButton tulajdonságnál pedig a Mégsem gombot! - Érdemes a Mégsem gomb megnyomásakor az alapértelmezett szöveget visszaadni üres string helyett. 169
170 RAJZOLÁS WINDOWS FORMON FELADATOK ÖSSZETETTEBB FELADAT 12.1.Modellezzük a mobiltelefonok adótornyait és az általuk lefedett területeket! Helyezzünk el 1-6 adótornyot egy 10*10-es mátrixszal modellezett területen, és határozzuk meg, hogy melyik torony melyik területrészt fedi le! Jelöljük különböző (világos) színekkel az adótornyokat, és azonos (a távolsággal arányosan sötétedő) színnel a többi területet! (12_Adotornyok) 45. ábra Adótornyok feladat megoldása A feladat megoldásának lépései: 1. Tervezzük meg a program felhasználói felületét, az ábrának megfelelően! Helyezzünk el egy 400*400 pixeles PictureBox objektumot a Formon! Az adótornyok számát NumericUpDown vezérlővel lehessen beállítani, 1 és 6 között. Ne lehessen számot kézzel beírni! 2. Vegyük fel a szükséges változókat! Mivel rajzolni fogunk, ezért szükségünk lesz egy Graphics és egy Bitmap objektumra. Ezeket a Form konstruktorában inicializálhatjuk. Töröljük le feketével a PictureBox hátterét! g.clear(color.fromargb(0, 0, 0)); pb1.image = bmp; Vegyünk fel egy Random objektumot is! 170
Bevezetés a Visual C# 2008 használatába
Szakmacsoportos alapozó gyakorlatok informatika területre Bevezetés a Visual C# 2008 használatába 11. évfolyam TANULÓI JEGYZET A TISZK rendszer továbbfejlesztése Petrik TISZK TÁMOP-2.2.3-07/1-2F-2008-0011
Bevezetés a Visual C# 2008 használatába
Szakmacsoportos alapozó gyakorlatok informatika területre Bevezetés a Visual C# 2008 használatába 11. évfolyam TANULÓI JEGYZET A TISZK rendszer továbbfejlesztése Petrik TISZK TÁMOP-2.2.3-07/1-2F-2008-0011
A C# programozási nyelv alapjai
A C# programozási nyelv alapjai Tisztán objektum-orientált Kis- és nagybetűket megkülönbözteti Ötvözi a C++, Delphi, Java programozási nyelvek pozitívumait.net futtatókörnyezet Visual Studio fejlesztőkörnyezet
Programozás BMEKOKAA146. Dr. Bécsi Tamás 8. előadás
Programozás BMEKOKAA146 Dr. Bécsi Tamás 8. előadás Visszatekintés A Windows Console alkalmazások egy karakteres képernyőt biztosítottak, ahol a kimenet a kiírt szöveg, míg a bemenet a billentyűzet volt.
BEVEZETÉS A VISUAL C# 2008 HASZNÁLATÁBA
Szakmacsoportos alapozó gyakorlatok informatika területre Informatikai gyakorlatok TÁMOP-2.2.3-07/1-2F-2008-0011 BEVEZETÉS A VISUAL C# 2008 HASZNÁLATÁBA Informatikai gyakorlatok 11. évfolyam TANÁRI KÉZIKÖNYV
Tömbök kezelése. Példa: Vonalkód ellenőrzőjegyének kiszámítása
Tömbök kezelése Példa: Vonalkód ellenőrzőjegyének kiszámítása A számokkal jellemzett adatok, pl. személyi szám, adószám, taj-szám, vonalkód, bankszámlaszám esetében az elírásból származó hibát ún. ellenőrző
Adabáziselérés ODBC-n keresztül utasításokkal C#-ban
Adabáziselérés ODBC-n keresztül utasításokkal C#-ban 1. Előkészítés Access adatbázis lemásolása, ODBC DSN létrehozása Másoljuk le az alábbiakat: Mit Honnan Hova list.mdb p:\johanyák Csaba\Vizualis programozas\data\
Tájékoztató. Használható segédeszköz: -
A 12/2013. (III. 29.) NFM rendelet szakmai és vizsgakövetelménye alapján. Szakképesítés, azonosítószáma és megnevezése 54 481 06 Informatikai rendszerüzemeltető Tájékoztató A vizsgázó az első lapra írja
BME MOGI Gépészeti informatika 13.
BME MOGI Gépészeti informatika 13. 1. feladat Készítsen alkalmazást, mely elvégzi a következő feladatokat! a. Állítson elő adott intervallumba eső, adott számú véletlen számot, és írja ki a számokat egy
// keressük meg a legnagyobb faktoriális értéket, ami kisebb, // mint százmillió
BME MOGI Gépészeti informatika 3. 1. feladat Végezze el a következő feladatokat! Kérjen be számokat 0 végjelig, és határozza meg az átlagukat! A feladat megoldásához írja meg a következő metódusokat! a.
Informatika terméktervezőknek
Informatika terméktervezőknek C# alapok Névterület (namespace) using Osztály (class) és Obejtumok Metódus (function, procedure, method) main() static void string[] arg Szintaxis // /* */ \n \t Névadások
1. Egyszerű (primitív) típusok. 2. Referencia típusok
II. A Java nyelv eszközei 1. Milyen eszközöket nyújt a Java a programozóknak Korábban már említettük, hogy a Java a C nyelvből alakult ki, ezért a C, C++ nyelvben járatos programozóknak nem fog nehézséget
BME MOGI Gépészeti informatika 4.
BME MOGI Gépészeti informatika 4. 1. feladat önálló feladatmegoldás Generáljon két 1 és 10 közötti véletlen egész számot, majd kiírja ezekre a számokra a tízes szorzótáblákat! Ha az első generált szám
Bevezetés a programozásba I.
Elágazás Bevezetés a programozásba I. 2. gyakorlat, tömbök Surányi Márton PPKE-ITK 2010.09.14. Elágazás Elágazás Eddigi programjaink egyszer ek voltak, egy beolvasás (BE: a), esetleg valami m velet (a
A szerzõrõl... xi Bevezetés... xiii
TARTALOMJEGYZÉK A szerzõrõl...................................................... xi Bevezetés...................................................... xiii I. rész A Visual Basic 2005 környezet 1. óra Irány
BME MOGI Gépészeti informatika 1.
BME MOGI Gépészeti informatika 1. 1. feladat Végezze el a következő feladatokat! Olvassa be a nevét és írjon üdvözlő szöveget a képernyőre! Generáljon két 1-100 közötti egész számot, és írassa ki a hányadosukat
BME MOGI Gépészeti informatika 7.
BME MOGI Gépészeti informatika 7. 1. feladat Írjon Windows Forms alkalmazást egy kör és egy pont kölcsönös helyzetének vizsgálatára! A feladat megoldásához hozza létre a következő osztályokat! Pont osztály:
Johanyák Zsolt Csaba: Ugráló gomb oktatási segédlet Copyright 2008 Johanyák Zsolt Csaba
Ugráló gomb Készítsünk egy egyszerű játékprogramot, ami egy mozgó nyomógombot tartalmaz. A nyomógomb beállított ideig marad egy helyben, majd az ablakon számára elhatárolt terület (panel) egy véletlenszerűen
Programozás I. Matematikai lehetőségek Műveletek tömbökkel Egyszerű programozási tételek & gyakorlás V 1.0 OE-NIK,
Programozás I. Matematikai lehetőségek Műveletek tömbökkel Egyszerű programozási tételek & gyakorlás OE-NIK, 2013 1 Hallgatói Tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk
Szoftvertervezés és -fejlesztés I.
Szoftvertervezés és -fejlesztés I. Operátorok Vezérlési szerkezetek Gyakorlás 1 Hallgatói Tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk a számonkérendő anyag vázlatát képezik.
Operációs rendszerek. 11. gyakorlat. AWK - szintaxis, vezérlési szerkezetek UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED
UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED AWK - szintaxis, vezérlési szerkezetek Operációs rendszerek 11. gyakorlat Szegedi Tudományegyetem Természettudományi és Informatikai Kar Csuvik
Johanyák Zsolt Csaba: Grafikus felület programozása. http://www.johanyak.hu e-mail: johanyak.csaba@gamf.kefo.hu Copyright 2008 Johanyák Zsolt Csaba
Johanyák Zsolt Csaba: Grafikus felület programozása http://www.johanyak.hu e-mail: johanyak.csaba@gamf.kefo.hu Copyright 2008 Johanyák Zsolt Csaba 1. Gyümölcsárazó automata Készítsünk egy gyümölcsárazó
Gyakorló feladatok Gyakorló feladatok
Gyakorló feladatok előző foglalkozás összefoglalása, gyakorlató feladatok a feltételes elágazásra, a while ciklusra, és sokminden másra amit eddig tanultunk Változók elnevezése a változók nevét a programozó
Webprogramozás szakkör
Webprogramozás szakkör Előadás 5 (2012.04.09) Programozás alapok Eddig amit láttunk: Programozás lépései o Feladat leírása (specifikáció) o Algoritmizálás, tervezés (folyamatábra, pszeudokód) o Programozás
BME MOGI Gépészeti informatika 2.
BME MOGI Gépészeti informatika. 1. feladat Generáljon egy 1 és 100 közötti véletlen egész számot, melyre a felhasználó tippelhet. A tippet a program értékelje a Sok vagy a Kevés visszajelzéssel. Ha a felhasználó
BME MOGI Gépészeti informatika 5.
BME MOGI Gépészeti informatika 5. 1. feladat Készítsen alkalmazást, mely feltölt egy egydimenziós tömböt adott tartományba eső, véletlenszerűen generált egész értékekkel! Határozza meg a legkisebb és a
OOP #14 (referencia-elv)
OOP #14 (referencia-elv) v1.0 2003.03.19. 21:22:00 Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj. e-mail: aroan@ektf.hu web: http://aries.ektf.hu/~aroan OOP OOP_14-1 - E jegyzet
VARIO Face 2.0 Felhasználói kézikönyv
VARIO Face 2.0 Felhasználói kézikönyv A kézikönyv használata Mielőtt elindítaná és használná a szoftvert kérjük olvassa el figyelmesen a felhasználói kézikönyvet! A dokumentum nem sokszorosítható illetve
Programozás alapjai gyakorlat. 2. gyakorlat C alapok
Programozás alapjai gyakorlat 2. gyakorlat C alapok 2016-2017 Bordé Sándor 2 Forráskód, fordító, futtatható állomány Először megírjuk a programunk kódját (forráskód) Egyszerű szövegszerkesztőben vagy fejlesztőkörnyezettel
1. Jelölje meg az összes igaz állítást a következők közül!
1. Jelölje meg az összes igaz állítást a következők közül! a) A while ciklusban a feltétel teljesülése esetén végrehajtódik a ciklusmag. b) A do while ciklusban a ciklusmag után egy kilépési feltétel van.
GenerikusOsztály<objektumtípus> objektum = new GenerikusOsztály<objektumtípus>();
BME MOGI Gépészeti informatika 17. A C# nyelv generikus típusait a System.Collections.Generics névtérben találhatjuk meg. Ez a névtér számos osztályt és interfészt tartalmaz, amelyek lehetővé teszik előre
AWK programozás, minták, vezérlési szerkezetek
10 AWK programozás, minták, vezérlési szerkezetek AWK adatvezérelt szkriptnyelv text processing, adat kiterjesztés, tagolt adatok automatizált soronkénti feldolgozása a forrásállományt soronként beolvassa
BME MOGI Gépészeti informatika 6.
BME MOGI Gépészeti informatika 6. 1. feladat Készítsen Windows Forms alkalmazást véletlen adatokkal létrehozott körök kölcsönös helyzetének vizsgálatára! Hozza létre a következő struktúrákat, melynek elemei
Változók. Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai):
Python Változók Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai): Név Érték Típus Memóriacím A változó értéke (esetleg más attribútuma is) a program futása alatt
1. feladat Készítse el szövegszerkesztővel, majd mentse osztály.txt néven a következő tartalmú szöveges fájlt:
BME MOGI Gépészeti informatika 12. 1. feladat Készítse el szövegszerkesztővel, majd mentse osztály.txt néven a következő tartalmú szöveges fájlt: Matematika;Fizika;Történelem;Irodalom;Nyelvtan;Angol;Testnevelés;
1. Olvassuk be két pont koordinátáit: (x1, y1) és (x2, y2). Határozzuk meg a két pont távolságát és nyomtassuk ki.
Számítás:. Olvassuk be két pont koordinátáit: (, y) és (2, y2). Határozzuk meg a két pont távolságát és nyomtassuk ki. 2. Olvassuk be két darab két dimenziós vektor komponenseit: (a, ay) és (b, by). Határozzuk
Bevezetés a programozásba I 4. gyakorlat. PLanG: Szekvenciális fájlkezelés. Szekvenciális fájlkezelés Fájlok használata
Pázmány Péter Katolikus Egyetem Információs Technológiai Kar Bevezetés a programozásba I 4. gyakorlat PLanG: 2011.10.04. Giachetta Roberto groberto@inf.elte.hu http://people.inf.elte.hu/groberto Fájlok
Tájékoztató. Használható segédeszköz: -
A 35/2016. (VIII. 31.) NFM rendelet szakmai és vizsgakövetelménye alapján. Szakképesítés, azonosító száma és megnevezése 54 481 06 Informatikai rendszerüzemeltető Tájékoztató A vizsgázó az első lapra írja
Készítette: Nagy Tibor István
Készítette: Nagy Tibor István A változó Egy memóriában elhelyezkedő rekesz Egy értéket tárol Van azonosítója (vagyis neve) Van típusa (milyen értéket tárolhat) Az értéke értékadással módosítható Az értéke
Változók. Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai):
Javascript Változók Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai): Név Érték Típus Memóriacím A változó értéke (esetleg más attribútuma is) a program futása
1. Alapok. #!/bin/bash
1. oldal 1.1. A programfájlok szerkezete 1. Alapok A bash programok tulajnképpen egyszerű szöveges fájlok, amelyeket bármely szövegszerkesztő programmal megírhatunk. Alapvetően ugyanazokat a at használhatjuk
7. fejezet: Mutatók és tömbök
7. fejezet: Mutatók és tömbök Minden komolyabb programozási nyelvben vannak tömbök, amelyek gondos kezekben komoly fegyvert jelenthetnek. Először is tanuljunk meg tömböt deklarálni! //Tömbök használata
Vezérlési szerkezetek
Vezérlési szerkezetek Szelekciós ok: if, else, switch If Segítségével valamely ok végrehajtását valamely feltétel teljesülése esetén végezzük el. Az if segítségével valamely tevékenység () végrehajtását
1. Feladat: beolvas két számot úgy, hogy a-ba kerüljön a nagyobb
1. Feladat: beolvas két számot úgy, hogy a-ba kerüljön a nagyobb #include main() { int a, b; printf( "a=" ); scanf( "%d", &a ); printf( "b=" ); scanf( "%d", &b ); if( a< b ) { inttmp = a; a =
SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS
SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS 2.ELŐADÁS A VB programozási nyelv Az Excel programozása 2 A VB programozási nyelv Adattípusok Adatok kezelése Vezérlőszerkezetek Adattípusok és műveletek Egész adattípusok
Választó lekérdezés létrehozása
Választó lekérdezés létrehozása A választó lekérdezés egy vagy több rekordforrásból származó adatokat jelenít meg. A választó lekérdezések a táblák, illetve az adatbázis tartalmát nem változtatják meg,
Bevezetés a programozásba I.
Bevezetés a programozásba I. 6. gyakorlat C++ alapok, szövegkezelés Surányi Márton PPKE-ITK 2010.10.12. Forrásfájlok: *.cpp fájlok Fordítás: a folyamat, amikor a forrásfájlból futtatható állományt állítunk
Objektumorientált Programozás III.
Objektumorientált Programozás III. Vezérlési szerkezetek ismétlés Matematikai lehetőségek Feladatok 1 Hallgatói Tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk a számonkérendő
Sorosítás (szerializáció) és helyreállítás. 1. Bináris sorosítás és helyreállítás. 1.1. Szükséges névterek. 1.2. Attribútumok. 1.3.
Sorosítás (szerializáció) és helyreállítás Cél: a memóriában tárolt adatok egyszerű lemezre mentése és visszatöltése. A sorosítás során létrehozunk egy állományt és egy sorosítást kezelő objektumot. Ez
A Windows az összetartozó adatokat (fájlokat) mappákban (könyvtárakban) tárolja. A mappák egymásba ágyazottak.
Mappakezelés WINDOWS-7 A Windows az összetartozó adatokat (fájlokat) mappákban (könyvtárakban) tárolja. A mappák egymásba ágyazottak. A PC legnagyobb mappája, amely az összes többi mappát is magában foglalja,
Felvételi tematika INFORMATIKA
Felvételi tematika INFORMATIKA 2016 FEJEZETEK 1. Természetes számok feldolgozása számjegyenként. 2. Számsorozatok feldolgozása elemenként. Egydimenziós tömbök. 3. Mátrixok feldolgozása elemenként/soronként/oszloponként.
7. Előadás. Makrók alkalmazása. Salamon Júlia. Előadás I. éves mérnök hallgatók számára
7. Előadás Makrók alkalmazása. Salamon Júlia Előadás I. éves mérnök hallgatók számára Feltételes ciklusok Ha a ciklusváltozó intervallumát, előre nem tudjuk mert például a program futása során megszerzett
3. Ezután a jobb oldali képernyő részen megjelenik az adatbázistábla, melynek először a rövid nevét adjuk meg, pl.: demo_tabla
1. Az adatbázistábla létrehozása a, Ha még nem hoztunk létre egy adatbázistáblát sem, akkor a jobb egérrel a DDIC-objekt. könyvtárra kattintva, majd a Létrehozás és az Adatbázistábla menüpontokat választva
Java programozási nyelv
Java programozási nyelv 2. rész Vezérlő szerkezetek Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2005. szeptember A Java programozási nyelv Soós Sándor 1/23 Tartalomjegyzék
Bevezetés a programozásba I 4. gyakorlat. PLanG: Szekvenciális fájlkezelés
Pázmány Péter Katolikus Egyetem Információs Technológiai Kar Bevezetés a programozásba I 4. gyakorlat PLanG: 2011.10.04. Giachetta Roberto groberto@inf.elte.hu http://people.inf.elte.hu/groberto Fájlok
DuneHD.hu. Kompatibilis médialejátszók: Dune HD Center Dune BD Prime Dune HD Base 2.0 Dune HD Base 3.0 Dune BD Prime 3.0
A Zappiti egy donationware, vagyis ingyenes program, mellyel kibővítheted Dune médialejátszód képességeit. A leírás a Zappiti 1.2.1 Beta változata alapján készült. Kompatibilis médialejátszók: Dune HD
1. DVNAV letöltése és telepítése
1. DVNAV letöltése és telepítése A Drén és Valner Szoftver Kft által készített DVNAV programmal lehetőség van a számlázó program által elkészített XML-ek NAV-hoz történő eljuttatására, majd a számlákról
CareLink Personal telepítési útmutató. Első lépések a CareLink Personal adatfeltöltéshez
CareLink Personal telepítési útmutató Első lépések a CareLink Personal adatfeltöltéshez A CareLink USB illesztőprogram telepítése A CareLink USB illesztőprogramot telepíteni kell. Ez az illesztőprogram
Dokumentum létrehozása/módosítása a portálon:
Dokumentum létrehozása/módosítása a portálon: 1. Belépés a szerkesztőfelületre A következő webcímre belépve - http://www.zalaegerszeg.hu/licoms/login - megjelenik a szerkesztőfelület beléptető ablaka,
Gyakorló 9. feladat megoldási útmutató
Gyakorló 9. feladat megoldási útmutató 1. Minek a leírása a következő? Aktív hálózati hardver eszközök (pl.: routerek) és szoftverek segítségével létrehozott biztonsági rendszer két hálózat (jellemzően
C#---Access adatbáziskezelési gyakorlat
C#---Access adatbáziskezelési gyakorlat 1. Feladat: adatbázis kezelő alkalmazás készítése C# környezetben, Access adatbázist használva. 2. Minta adatbázis létrehozása ACCESS-ben 3. A Projekt létrehozása
Számítástechnika II. BMEKOKAA Előadás. Dr. Bécsi Tamás
Számítástechnika II. BMEKOKAA153 2. Előadás Dr. Bécsi Tamás Tömbök (Arrays) Definíció: típus[] név; (pld. int[] szamok; ) Inicializálás: int[] szamok = new int[4]; int[] szamok = 1,2,4,3,5}; int[] szamok
HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport
10-es Keressünk egy egész számokat tartalmazó négyzetes mátrixban olyan oszlopot, ahol a főátló alatti elemek mind nullák! Megolda si terv: Specifika cio : A = (mat: Z n m,ind: N, l: L) Ef =(mat = mat`)
Access gyakorlati feladatok lépésről lépésre
Access gyakorlati feladatok lépésről lépésre 1. feladat: Hajómenetrend A balatoni hajómenetrend rendelkezésünkre áll a menetrend.txt állományban. Készítsen új adatbázist HAJO néven! A mellékelt adatállományt
3. Osztályok II. Programozás II
3. Osztályok II. Programozás II Bevezető feladat Írj egy Nevsor osztályt, amely legfeljebb adott mennyiségű nevet képes eltárolni. A maximálisan tárolható nevek számát a konstruktorban adjuk meg. Az osztályt
Tartalom jegyzék 1 BEVEZETŐ 2 1.1 SZOFTVER ÉS HARDVER KÖVETELMÉNYEK 2 2 TELEPÍTÉS 2 3 KEZELÉS 5
Tartalom jegyzék 1 BEVEZETŐ 2 1.1 SZOFTVER ÉS HARDVER KÖVETELMÉNYEK 2 2 TELEPÍTÉS 2 3 KEZELÉS 5 3.1 ELSŐ FUTTATÁS 5 3.2 TULAJDONOSI ADATLAP 6 3.3 REGISZTRÁLÁS 6 3.4 AKTIVÁLÁS 6 3.5 MÉRÉS 7 3.5.1 ÜGYFÉL
A C# PROGRAMOZÁSI NYELV
A C# PROGRAMOZÁSI NYELV 2010.02.23. Bevezetés C# nyelv jellemzői 2 Kis és NAGY betű érzékeny Minden utasítást pontos vessző zár. Utasítás zárójel a:,. .NET Framework keretrendszerek 3 Microsoft.NET Framework
Ablakok. Fájl- és mappaműveletek. Paint
Ablakok. Fájl- és mappaműveletek. Paint I. Ablakok I.1. Ablak fogalma Windows = ablak, rövidítése: win Program indítás kinyílik az ablaka I.2. Ablak részei A programablak rendszerint az alábbi részekre
Információs technológiák 2. Gy: CSS, JS alapok
Információs technológiák 2. Gy: CSS, JS alapok 1/69 B ITv: MAN 2017.10.01 Ismétlés Van egy Web nevű mappánk, ebben vannak az eddig elkészített weboldalak (htm, html) képek (jpg, png). Logikai felépítés
Java II. I A Java programozási nyelv alapelemei
Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2008. 02. 19. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve az annak
Szoftvertechnolo gia gyakorlat
Szoftvertechnolo gia gyakorlat Dr. Johanyák Zsolt Csaba http://johanyak.hu 1. Dependency Injection (függőség befecskendezés) tervezési minta A tervezési minta alapgondolata az, hogy egy konkrét feladatot
Felhasználói dokumentáció. a TávTagTár programhoz. Készítette: Nyíri Gábor, hdd@nc-studio.com GDF Abakusz regisztrációs kód: GDFAba43
a TávTagTár programhoz Készítette: Nyíri Gábor, hdd@nc-studio.com GDF Abakusz regisztrációs kód: GDFAba43 Tartalomjegyzék Futási feltételek... 3 Telepítés... 3 Indítás... 3 Főablak... 4 Új személy felvétele...
BME MOGI Gépészeti informatika 14.
BME MOGI Gépészeti informatika 14. 1. feladat Készítsen alkalmazást, mely a képernyő közepére egy véletlen színnel kitöltött kört rajzol! A színváltást nyomógomb segítségével oldja meg! A rajzolást a form
INFORMATIKAI ALAPISMERETEK
ÉRETTSÉGI VIZSGA 2005. május 20. INFORMATIKAI ALAPISMERETEK KÖZÉPSZINTŰ ÉRETTSÉGI VIZSGA Az írásbeli vizsga időtartama: 180 perc JAVÍTÁSI-ÉRTÉKELÉSI ÚTMUTATÓ OKTATÁSI MINISZTÉRIUM Megoldási útmutató I.
Aromo Szöveges értékelés normál tantárggyal
Aromo Szöveges értékelés normál tantárggyal Aromo Iskolaadminisztrációs Szoftver Felhasználói kézikönyv -- Szöveges értékelés 1 Tartalomjegyzék Aromo Szöveges értékelés normál tantárggyal 1 Bevezetés 3
C programozási nyelv Pointerek, tömbök, pointer aritmetika
C programozási nyelv Pointerek, tömbök, pointer aritmetika Dr. Schuster György 2011. június 16. C programozási nyelv Pointerek, tömbök, pointer aritmetika 2011. június 16. 1 / 15 Pointerek (mutatók) Pointerek
MS ACCESS 2010 ADATBÁZIS-KEZELÉS ELMÉLET SZE INFORMATIKAI KÉPZÉS 1
SZE INFORMATIKAI KÉPZÉS 1 ADATBÁZIS-KEZELÉS MS ACCESS 2010 A feladat megoldása során a Microsoft Office Access 2010 használata a javasolt. Ebben a feladatban a következőket fogjuk gyakorolni: Adatok importálása
Java II. I A Java programozási nyelv alapelemei
Java2 / 1 Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2009. 02. 09. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve
Java Programozás 5. Gy: Java alapok. Adatkezelő 1.rész
Java Programozás 5. Gy: Java alapok Adatkezelő 1.rész 27/1 B ITv: MAN 2018.04.13 Feladat Egy nagyon hosszú feladatmegoldásba kezdünk bele: létrehozunk egy adatkezelő programot, sok-sok extrával: 27/2 A
A 35/2016. (VIII. 31.) NFM rendelet szakmai és vizsgakövetelménye alapján.
A 35/2016. (VIII. 31.) NFM rendelet szakmai és vizsgakövetelménye alapján. Szakképesítés azonosítószáma és megnevezése 54 481 06 Informatikai rendszerüzemeltető Tájékoztató A vizsgázó az első lapra írja
Programozási nyelvek JAVA EA+GY 1. gyakolat
Programozási nyelvek JAVA EA+GY 1. gyakolat EÖTVÖS LORÁND TUDOMÁNYEGYTEM INFORMATIKAI KAR PROGRAMOZÁSI NYELVEK ÉS FORDÍTÓPROGRAMOK TANSZÉK 2018/2019. tavaszi félév Tartalom 1 A Java alapjai 2 Java program
Gyakorló feladatok az 1. nagy zárthelyire
Gyakorló feladatok az 1. nagy zárthelyire 2012. október 7. 1. Egyszerű, bevezető feladatok 1. Kérjen be a felhasználótól egy sugarat. Írja ki az adott sugarú kör kerületét illetve területét! (Elegendő
E-Freight beállítási segédlet
E-Freight beállítási segédlet Az E-Freight rendszer működéséhez szükséges programok és beállítások v08 A legújabb verzióért kérjük, olvassa be az alábbi kódot: 1. Támogatott böngészők Az E-Freight az Internet
Diagram létrehozása. 1. ábra Minta a diagramkészítéshez
Bevezetés Ebben a témakörben megtanuljuk, hogyan hozzunk létre diagramokat, valamint elsajátítjuk a diagramok formázásnak, módosításának lehetőségeit. A munkalap adatainak grafikus ábrázolási formáját
Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések. 1. Mi a programozás?
Bevezetés Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések Forráskód Hibajegyzék p2p.wrox.com xiii xiii xiv xiv xvi xvii xviii
Bánsághi Anna 2014 Bánsághi Anna 1 of 35
IMPERATÍV PROGRAMOZÁS Bánsághi Anna anna.bansaghi@mamikon.net 4. ELŐADÁS - ADATFOLYAMOK KEZELÉSE 2014 Bánsághi Anna 1 of 35 TEMATIKA I. ALAPFOGALMAK, TUDOMÁNYTÖRTÉNET II. IMPERATÍV PROGRAMOZÁS Imperatív
TERC V.I.P. hardverkulcs regisztráció
TERC V.I.P. hardverkulcs regisztráció 2014. második félévétől kezdődően a TERC V.I.P. költségvetés-készítő program hardverkulcsát regisztrálniuk kell a felhasználóknak azon a számítógépen, melyeken futtatni
Python tanfolyam Python bevezető I. rész
Python tanfolyam Python bevezető I. rész Mai tematika Amiről szó lesz (most): Interpretált vs. fordított nyelvek, GC Szintakszis Alaptípusok Control flow: szekvencia, szelekció, iteráció... Függvények
Programozás alapjai. (GKxB_INTM023) Dr. Hatwágner F. Miklós augusztus 29. Széchenyi István Egyetem, Gy r
Programozás alapjai (GKxB_INTM023) Széchenyi István Egyetem, Gy r 2019. augusztus 29. Feladat: írjuk ki az els 10 természetes szám négyzetét! #i n c l u d e i n t main ( v o i d ) { p r
BASH script programozás II. Vezérlési szerkezetek
06 BASH script programozás II. Vezérlési szerkezetek Emlékeztető Jelölésbeli különbség van parancs végrehajtása és a parancs kimenetére való hivatkozás között PARANCS $(PARANCS) Jelölésbeli különbség van
ALAPOK. 0 és 255 közé eső számértékek tárolására. Számértékek, például távolságok, pontszámok, darabszámok.
ADATBÁZIS-KEZELÉS ALAPOK Főbb Adattípusok: Igen/Nem Bájt Ez az adattípus logikai adatok tárolására alkalmas. A logikai adatok mindössze két értéket vehetnek fel. (Igen/Nem, Igaz/Hamis, Férfi/Nő, Fej/Írás
Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek
Programozás alapjai gyakorlat 4. gyakorlat Konstansok, tömbök, stringek Házi ellenőrzés (f0069) Valósítsd meg a linuxos seq parancs egy egyszerűbb változatát, ami beolvas két egész számot, majd a kettő
Programozás alapjai C nyelv 4. gyakorlat. Mit tudunk már? Feltételes operátor (?:) Típus fogalma char, int, float, double
Programozás alapjai C nyelv 4. gyakorlat Szeberényi Imre BME IIT Programozás alapjai I. (C nyelv, gyakorlat) BME-IIT Sz.I. 2005.10.10.. -1- Mit tudunk már? Típus fogalma char, int, float,
Bevezetés a programozásba I.
Bevezetés a programozásba I. 5. gyakorlat Surányi Márton PPKE-ITK 2010.10.05. C++ A C++ egy magas szint programozási nyelv. A legels változatot Bjarne Stroutstrup dolgozta ki 1973 és 1985 között, a C nyelvb
Rövid leírás a Make Your Mark szoftver használatához
Rövid leírás a Make Your Mark szoftver használatához Ahhoz, hogy egy gyors példán keresztül bemutassunk, a program működését, egy Plytex címkét hozunk létre. Először létre kell hozni egy címkét, majd kinyomtatni
10. gyakorlat Struktúrák, uniók, típusdefiníciók
10. gyakorlat Struktúrák, uniók, típusdefiníciók Házi - (f0218) Olvass be 5 darab maximum 99 karakter hosszú szót úgy, hogy mindegyiknek pontosan annyi helyet foglalsz, amennyi kell! A sztringeket írasd
Occam 1. Készítette: Szabó Éva
Occam 1. Készítette: Szabó Éva Párhuzamos programozás Egyes folyamatok (processzek) párhuzamosan futnak. Több processzor -> tényleges párhuzamosság Egy processzor -> Időosztásos szimuláció Folyamatok közötti
BME MOGI Gépészeti informatika 18. Grafika, fájlkezelés gyakorló óra. 1. feladat Készítsen alkalmazást az = +
BME MOGI Gépészeti informatika 18. Grafika, fájlkezelés gyakorló óra 1. feladat Készítsen alkalmazást az = + függvény ábrázolására! Az értelmezési tartomány a [-6;5] intervallum, a lépésköz 0,1 legyen!
AWK programozás Bevezetés
09 AWK programozás Bevezetés AWK adatvezérelt szkriptnyelv text processing, adat kiterjesztés, tagolt adatok automatizált soronkénti feldolgozása a forrásállományt soronként beolvassa és feldolgozhatóvá
CIB Internet Bank asztali alkalmazás Hasznos tippek a telepítéshez és a használathoz Windows operációs rendszer esetén
CIB Internet Bank asztali alkalmazás Hasznos tippek a telepítéshez és a használathoz Windows operációs rendszer esetén A CIB Internet Bankba való webes felületen keresztül történő belépés az Oracle által