Ciklusszervező utasítások minden programozási nyelvben léteznek, így például a LOGO-ban is. LOGO nyelven, (vagy legalábbis LOGO-szerű nyelven) írt programok gyakran szerepelnek az iskola számítástechnikai versenyein. Ilyen például a következő feladat is! Mit rajzol és milyen méretben az alábbi LOGO nyelvű program? Kezdetben a „toll” a papír közepén van és balra (nyugat felé) mutat. Az egyes utasítások jelentése: LEFT f, RIGHT f: FORWARD x BACK x: REPEAT n [utasítások]:
balra illetve jobbra fordulás f fokkal. hatására előre lép x egységgel a toll, hatására hátra lép x egységgel a toll hatására pedig a zárójelbe tett utasításokat n-szer megismétli.
Képzeljük tehát azt, hogy egy toll van a kezünkben, és egy balra indul a rajzolás. Ha ez nem megy, képzeljünk egy teknőst, akinek a feje tintás, és balra akar menni, de csak a LOGO utasításokat érti. REPEAT 2 [REPEAT 3 [RIGHT 120 FORWARD 10] BACK 10 LEFT 60] REPEAT 3 [FORWARD 10 RIGHT 120] RIGHT 180 REPEAT 3 [FORWARD 10 LEFT 120] Itt a REPEAT (Ismételd) 2 egy olyan előírt lépésszámú ciklust jelent, amelynél a zárójelbe írt utasításokat (a ciklusmagot) 2x kell megcsinálni (csak nincs ciklusváltozó). Mivel a REPEAT 2-n belül van egy másik REPEAT is, így egymásba ágyazott ciklusokról beszélünk. Ez a program csinál valamit 3x, de azt még 2x megismétli. Ezt jelenti lényegében a REPEAT 2 [REPEAT 3 [] ]. Már csak a valamiket kell megérteni. ☺ A fenti algoritmus tehát a teknőst észak-kelet felé fordítja (RIGHT 120, a RIGHT 90 észak felé fordítaná)), majd előre mászik 10 egységet, azaz húz 10 egység vonalat. Ezt csinálja meg 3x. Ha ezt végigcsinálja a teknős, akkor végül is egy háromszöget rajzolt, amelynek szögei 60 fokosak, oldalai 10 hosszúak. Amikor ennek a ciklusnak vége, akkor a háromszög bal alsó csúcsánál van a teknős és továbbra is nyugat felé néz. Aztán visszatolat 10 egységet (jobboldali csúcs), majd balra fordul 60-at. És itt van vége a REPEAT 2 ciklusnak. A teknős a háromszög jobboldali csúcsánál van, és DNY (dél-nyugat) felé néz, egészen pontosan 60 fokra fordult balra NY-tól. (Lásd ábra). Most pedig 3x csinálja azt, amit korábban, hiszen a REPEAT 3 ciklusmagját kell végrehajtania. Azaz jobbra fordul 120 fokot, vagyis a korábbi háromszög jobb oldali szárán fog megindulni a teknős. És megint rajzol egy háromszöget. A végén a teknős az ábrán látható irányba néz (DNY), de a BACK 10 és a LEFT 60 miatt végül DK felé mutat (felső nyíl). Megint következik egy REPEAT 3 ciklus, amelynél a ciklusmag megint háromszög rajzolás.
Az eredmény itt látható. Az utolsó ciklus még egy háromszöget rajzol, az eddigiek tetejére.
A megoldás tehát az ábrán látható 4 háromszög, amelynek minden oldal 10 egység hosszú, szögei 60 fokosak. (A rajz nem elég, az adatok is kellenek!)
1
Tömb Általában statikus adatszerkezet, ami annyit jelent, hogy a tömb elemeinek száma rögzített (elemek száma nem változtatható). A tömbnél a logikai összefüggést az adatelemek között azok egymáshoz viszonyított elhelyezkedése adja (ez adja a szerkezetet). Beszélhetünk egydimenziós, kétdimenziós stb. tömbről. Minden esetben van a tömbnek legelső eleme (egy kitüntetett eleme) és valamilyen értelemben ehhez viszonyítjuk a többinek a helyét. Ez adja a szerkezetet és ez a szerkezet kötött. Pl.: Egydimenziós tömbben meg tudom mondani, hogy az elsőhöz képest hányadik az illető elem. Pl.: Kétdimenziós tömb (mátrixnak nevezik) esetén szintén az elsőhöz képest mondjuk meg, hogy az elsőhöz képest hányadik sor, hányadik oszlop - ha megmondom, hogy hol van, azt úgy hívjuk, hogy indexelés - adott szerkezetben adott elemre az indexével hivatkozom, az indexet pedig valamilyen értelemben mindig az elsőhöz viszonyítom - ilyen értelemben index lehet bármi (bármilyen jellegű hivatkozás) ami egyértelmű. Az elsőt kell rögzítenem valamilyen értelemben. Egydimenziós esetben, ha a tömb neve A, akkor az A[2] alatt a tömb 2. elemét értem Kétdimenziós esetben A[3,2] jelenti a mátrix 3. sorának, 2. oszlopában lévő elemét. Olyan programozási eszköz tehát, melyet neve, dimenziója vagy dimenziói, elemeinek típusa, indexeinek típusa és tartománya jellemez. A tömb neve egy tetszőleges azonosító, a programozó választja, akárcsak a dimenziók számát. A dimenziók számát illetően a nyelvek meg szoktak állni valahol, ez általában 255, néha 7. Háromdimenziós tömbbel szinte minden probléma megoldható, ennek részei: sor, oszlop, lapok. Tovább már szemlélet nincs, nem is kell, de lehet (pl. idő). Lényeges, hogy a tömb elemei milyen típusúak lehetnek. Nem minden nyelv engedi meg, hogy bármilyen tömb tetszőleges elemekből álljon, a PASCAL nagyjából igen. A PASCAL-ban tetszőleges típusú tömböket lehet felépíteni a megadott elemekből, kivétel a Fájl. Lényeges az is a nyelvekben, hogy az indexek típusa milyen lehet. Szinte minden nyelv tudja kezelni az egész típusú indexekkel rendelkező tömböket. A PASCAL-ban az indexek típusa bármilyen sorszámozott típus lehet. Nyelvfüggő, hogy az indexek tartománya mekkora lehet, különös tekintettel arra, hogy az indexek tartományát hogyan lehet előírni. Egyes programozási nyelveknél az indexek tartományának alsó határa rögzített a nyelv által, nem befolyásolhatom. Más nyelvek esetében a programozónak kell megadnia az index típusát és tartományát, vagyis az indexeknél meg kell adni egy alsó és egy felső határt, amivel definiálhatjuk az indexek tartományát. Ilyen pl. a PASCAL is. A tömb egészére a nevével hivatkozhatunk. A tömb egy elemére pedig úgy, hogy a név mellett megadjuk az illető elem indexét vagy indexeit. név[index(ek)] Ha kifejezéssel adom meg az indexet, akkor a kifejezés értékének a deklarált indexhatárok tartományába kell esni, valamint a kifejezés típusának meg kell egyezni az index definícióban megadott típussal. Vannak olyan nyelvek, amelyek lehetővé teszik, hogy többdimenziós tömbnél valamelyik dimenzióban elhelyezkedő összes elemre egyszerre lehessen hivatkozni. Pl. kétdimenziós tömbnél egy sorra lehet hivatkozni. A PASCAL is ilyen. Lényeges még - a hivatkozási nyelv vagy az implementáció dönti el -, hogy a tárban hogyan jelenik meg a tömb, hogyan tárolódik a tömb? Válasz: sorfolytonosan vagy oszlopfolytonosan. Sorfolytonos akkor, ha felírásnál a legutolsó index változik a leggyorsabban. PASCAL-nál az elhelyezés sorfolytonos. Ez nyelvfüggő, legelőször mindig a legelső elem tárolódik. A tárolás többdimenziós tömbnél lényeges. Lényeges tudni, hogy sor, vagy oszlopfolytonos a tárolás, mert a tömb nevének leírása az összes elemre való hivatkozást jelenti és lényeges, hogy milyen sorrendben vannak az elemek. Ez többdimenziós tömb egydimenziós tömbre való leképezése. Bizonyos nyelvek tudnak olyan kifejezéseket kezelni, melyekben tömbneveket szerepeltethetek. Vannak olyan nyelvek, amelyek képesek a tömbökkel úgy műveleteket végezni, hogy a tömb minden elemével műveletet végeznek. Ilyen pl. a PL/I. A PASCAL nem ilyen, csak a tömbelemekkel lehet műveleteket végezni. Ha a tömb statikus, akkor az azt jelenti, hogy a fordítás pillanatában eldől a mérete. A deklarálásnál fix alsó és felső határokat adok, fix a méret és ez nem változtatható meg a futás során. A PASCAL-ban az indexek határa hozzátartozik a tömb típusához. A Turbo Pascalban az alábbi módon tudunk tömböket deklarálni:
Var Tömbnév: Array [i1..j1,i2..j2,...,in..jn] Of elemtípus; (Megjegyzés: ik<jk, ahol k=1,2,...,n) Az indexhatárok lehetnek negatívak, illetve 0 is. Az indexek típusa bármilyen sorszámozott típus lehet. Deklaráláskor az indexek lehetnek kifejezések is, de a kifejezések csak konstansokból és műveleti jelekből állhatnak. 2
Példa
Var Vektor: Array[-1..10] Of Byte; Itt egy Vektor nevű tömböt deklaráltunk, amelynek elemei bájt típusúak, azaz egész számokat tárolhatunk a tömbben. Ezek a számok a 0..,255 közötti egész számok lehetnek. A tömb legkisebb indexe a -1, a legnagyobb 10, azaz a tömb 12 elemű! (-1, 0, 1, 2,…,10 az indexek). A Vektor tehár egy dimenziós tömb. A Vektor[2] a tömb 2. elemét jelenti. A Vektor[-2] és a Vektor[11] érvénytelen hivatkozások, hiszen ennek a vektornak nincs mínuszkettedik eleme és tizenegyedik eleme sem!
Var a:Array[1..2,1..3,1..2] Of Byte; {Ez egy háromdimenziós tömb} b,c:Array['a'..'z'] Of String[22]; {A b és c egy vektor. } Matrix:Array[1..10,1..10] Of Longint; {10x10-es mátrix} Hivatkozás a tömbökre: Matrix[2,3]:=a[1,2,1]; vagy b[2]:=’Nincs ma csoki!’ A tömb elemeivel ugyanolyan műveleteket végezhetünk, mint más típusú változókkal. Például: b[1]:='Géza' Vektor[1]:=2*2; Vektor[4]:=Vektor[1]*3+2; Mivel a ‘b’ és a ‘c ‘ tömb azonos típusú és számú elemekből áll, így érvényes az alábbi értékadás is: b:=c; Itt most ‘b’ és ‘c’ tömbök, mégis lehetséges az értékadás az indexek megadása nélkül. Azaz a b vektor minden eleme vegye fel a c vektor elemeit. Az első az elsőt, a második a másodikat, stb. Mire is jók a tömbök? Természetesen adattárolásra. Ha például 10 vagy 100 adatot akarunk tárolni a programunk futása során, akkor nem vehetünk fel 100 darab változót. (Felvehetünk, de sokáig tart, és kezelni sem lehet könnyen.) Hogyan olvashatunk be a billentyűzetről 10 számot, és hogyan tárolhatjuk el ezeket egy vektorban? Íme a példa: Ciklus i:=1-től 10-ig Be: A[i] Ciklus vége Mit is jelent ez? Azt, hogy i értéke először egy lesz, majd a Be:A[i] utasításra megyünk. Mivel i értéke 1, így az A vektor 1. elemébe (A[1]) kerül a beolvasott szám. Aztán i értéke 2 lesz, így az A[2]-be fogunk beolvasni, és így tovább, i=10-ig. Hogyan lehet kiíratni ezt a vektort? Például így: Ki: A[1]; Ki:A[2]; Ki:A[3]; … De ezt értelmes ember nem csinálja, ha mégis, mégkérjük, hogy 1000 elemű vektor elemeit írassa így ki. ☺ A megoldás a következő: Ciklus i:=1-től 10-ig KI:A[i] Ciklus vége Az i értéke tehát először 1 lesz, majd a KI:A[i] hajtódik végre. Mivel i értéke 1, így KI:A[1] hajtódik végre, tehát az A vektor első eleme lesz kiírva. Aztán i értéke 2 lesz, majd A[2] kerül végrehajtásra, stb. Egy ilyen vektort például így lehet deklarálni a Pascalban: Var A: Array[1..10] Of Integer. Mondatszerű leírással: Változó A:Tömb[1..10] Egész. Ahogy előbb olvashattuk, a kétdimenziós tömb neve mátrix. Például egy 10*3-as mátrixba az alábbi módon lehet beolvasni adatokat a billentyűzetről: 3
Ciklus i:=1-től 4-ig Ciklus j:=1-től 3-ig Be: A[i,j] Ciklus vége Ciklus vége
Az A vektorba a következő számokat olvassuk be: 342 123 561 Ez itt az 789 A[3,3]
Először i értéke 1 lesz, j értéke pedig szintén 1. Így az A[1,1], azaz az A vektor első sorának, első oszlopába olvasunk be egy értéket (a példában a 3-at) . Aztán j értéke 2 lesz, így A[1,2]-be olvasunk be (ide a 4 kerül). Aztán j értéke 3 lesz, így az A[1,3]-ba olvasunk be. (Ez a kettő.) Ezután i értéke 2 lesz, j értéke megint 1, azaz A[2,1]-be (itt az 1 lesz) olvasunk be. Majd a belső ciklus követve: A[2,2], A[2,3] a következő két beolvasás. Ezután i értéke 3 lesz, j értéke megint 1, így A[3,1]-be (itt az 5 van) olvasunk be. Majd a belső ciklus követve: A[3,2], A[3,3] a következő két beolvasás. Egészen A[4,3]-ig tart a beolvasás. Ezt hívjuk sorfolytonos beolvasásnak, hiszen először az első sorba olvastunk be, aztán a 2-ba, stb. Kiíratni (sorfolytonosan) a következőképpen lehet ezt a mátrixot: Ciklus i:=1-től 4-ig Ciklus j:=1-től 3-ig KI: A[i,j] Ciklus vége Ciklus vége Először az A[1,1], aztán az A[1,2], aztán az A[1,3] lesz kiíratva, majd A[2,1], A[2,2], aztán az A[2,3], stb. Deklárni így lehet a mátrixot: Var A: Array[1..4,1..3] Of Integer. Mondatszerű leírással: Változó A:Tömb[1..10,1..3] Egész. Adott egy olyan B nevű mátrix, amelynek 4 sora és 4 oszlopa van! Nézzük meg az alábbi példát! Ciklus i:=1-től 4-ig Ciklus j:=1-től 4-ig Ki: B[j,i] Ciklus vége Ciklus vége
B nevű, 4*4-es mátrix: 3421 2103 2310 9812
Ez itt a B[3,4]. 3. sor, 4. oszlop.
Hogyan íródnak ki a mátrix elemei? Mivel először i és j is 1, így a B[1,1] lesz kiírva, azaz a 3. Aztán j értéke 2 lesz, így a B[2,1] lesz kiírva. Ez pedig a 2. Majd j értéke 3, aztán 4 lesz, így a B[3,1] és a B[4,1], vagyis 2 és 9 kerül kiírása. Aztán i értéke 2 lesz, j megint 1, így a B[1,2] vagyis a 4 lesz kiírva. Aztán a belső ciklus futása miatt j értéke 2,3,4 lesz, így a B[2,2], B[3,2], B[4,2] vagyis 1, 3, 8 értékek lesznek kiírva. Aztán i 3 lesz, j megint 1, így a B[1,3] kerül kiírásra, aztán a B[2,3], B[3,3], B[4,3], stb. Ha jól végig gondoljuk a dolgot, akkor először az első oszlop, aztán a második oszlop, végül a negyedik oszlop adatai kerültek kiírásra. Ezt hívjuk oszlopfolytonos feldolgozásnak, kiírásnak. Mivel a mátrixnak pontosan annyi sora van, amennyi oszlopa, így négyzetes mátrixnak nevezzük. A 3,1,1,2 értéke a mátrix főátlójában vannak. A főátló tehát a B[1,1], B[2,2], B[3,3], B[4,4]. A másik átló a B[1,4], B[2,3], B[3,2], B[4,1]. A mátrix főátlójában lévő elemek kiírása: Ciklus i:=1-től 4-ig Ki:B[i,i] Ciklus vége
4
A másik átlóban lévő elemek kiírása: Ciklus i:=1-től 4-ig Ki:B[i, 4-i+1] Ciklus vége Először i értéke 1, így a B[1,4-1+1], azaz a B[1,4] lesz kiíratva, majd i értéke kettő lesz, így B[2,4-2+1], azaz a B[2,3], stb.
5