Programozási tételek és alkalmazásaik Az ún. programozási tételek bevezetése és alkalmazása hagyományosan az egyik meghatározó pillére az ELTÉ-n folyó programozás oktatásának. A programozási tételek olyan általános célú, absztrakt szinten megfogalmazott egyszerű algoritmus minták, amelyek a tapasztalt fejlesztők szerint is a problémamegoldás és a programkészítés gyakran felhasznált elemi építőkövei. A jellegzetesen ELTE-s szemléletű programozás módszertan egyik korai, ám meghatározó forrása E. W. Dijkstra: A Discipline of Programming című könyve [1], amely 1973-ban jelent meg. A tantárgy bevezetését, kidolgozását és oktatását hosszú éveken végző Fóthi Ákos egyetemi tankönyve [2] teljeskörűen és letisztult formában tartalmazza mindazt, amit közel 30 évig tanultunk (mi, tanítványok és kollégák) az előadásaiból, egy korai, de már hivatalosan kiadott jegyzetéből [3], valamint a tananyag nagyszámú leírt változatából. A tantárgy jelenlegi oktatója Gregorics Tibor is részletesen összefoglalta egy kétkötetes könyvben [4] a tananyag időszerű tartalmát. A programozási tételek aktuális megfogalmazását is ebből a könyvből merítettem. (Hadd maradjak a személyes hangvételű egyes szám első személy mellett, végig.) A programozási tételek olyan általánosan megfogalmazott feladatokra adott (bizonyítottan helyes) absztrakt megoldások, amelyeket egy adott [m, n] egész intervallum és az azon értelmezett f függvény, illetve β tulajdonság fogalomrendszerében írunk fel. Már itt megjegyzem, hogy a feladat gyakran nem így, hanem egy A [1..n] tömbön megfogalmazva jelenik meg. Ilyenkor a tételben szereplő intervallumnak az [1..n] index tartomány felel meg, az f (i) függvényértékek pedig az A[i] tömb elemeiben nyernek elhelyezést. Ez még egy jól belátható absztrakciós lépés az eredeti megfogalmazáshoz képest, így a tömbös változatokat nem foglaljuk külön programozási tételekbe. Ezek az eredeti tételekből kevés módosítással könnyen felírhatók. (A [4] könyv szerzője általánosította a tételeket minden olyan struktúrára, amelyeken egy felsorolás definiálható. Ettől az általánosítástól itt most tekintsünk el.) A programozási tételek formája alig változott napjainkig. A korai verzióhoz képest változások történtek elsősorban a feladatspecifikáció felírásában (nem szerepel a paramétertér), illetve a ciklusszervezés módjában. Az utóbbi lényeges módosítás (korábban az intervallumon kívülről indult a ciklusváltozó, ma pedig az intervallum bal végpontjától). Felvetem majd egy olyan alternatív leírási mód lehetőségét, amelyet a nagy hatású és tekintélyes [5] Algoritmusok (az 1. kiadás fordításának a címe), illetve Új algoritmusok (a 2. kiadás fordításának a címe) könyvben alkalmaznak a szerzők (a szokásos akronímával: CLRS). Ez valamivel közelebb áll a kódolt formához, mint a programozási tételek eredeti változata. Pontosabban, úgy lehetne elhelyezni a szóban forgó CLRS-féle algoritmus leírási módot, mint amelyet az ELTE-s leírás utáni következő lépésben elkészítünk. Ez egy áthidaló állapot a (nálunk szokásos) magasabb absztrakciós szint és a (programozók által is elfogadható) programkód között. Magam is így oldok meg két lépésben iterálva kisebb méretű, oktatási célú feladatokat: (i) az ELTE-s programozási tételek magasabb absztrakciós szintjén; (ii) a CLRS könyv által használt absztrakt függvényekkel tovább lépve a kód felé. 1
Gyakran szembe állítják a struktogramot és a pszeudokódot. Pedig, nincs közöttük összebékíthetetlen ellentét. Noha a különbség több, mint tipográfiai: pl. gyakran máshogyan írunk fel egy IF-et az egyik, illetve a másik rendszerben, mivel a struktogramban nincs egyágú IF-THEN csak két-ágú IF-THEN-ELSE. Meglepő módon, olykor váratlanul kiderül, hogy az üres ELSE-ágakban árválkodó SKIP-ek komolyan támogatják az algoritmus működésének a megértést. Magam mindkettőt használom, de a struktogramot jóval többször, mivel jobbnak, kifejezőbbnek találom. A matematikus hallgatók, akik másodéves korukban találkoznak a struktogrammal, maguktól jutnak hasonló véleményre. Elég megnézni a feltételes maximumkeresés algoritmusának absztrakt leírását a kétféle módszer szerint. Bármilyen akár saját kialakítású leírási forma esetén is megszívlelendő az a figyelmeztetés, hogy tekintsük ezeket a megoldásokat csupán absztrakt algoritmusoknak, amelyek nem tartalmaznak előírásokat a kódolásra. A programkód létrehozása mindig egy önálló kreatív lépés, amely formai szempontból jórészt független az absztrakt megoldástól. A programozási tételeket a kisebb méretű feladatok esetén legtöbbször beszerkesztjük a programkódba (a konkrét feladatnak megfelelő módosítások után). Máskor meg külön eljárást alakítunk egy programozási tételből, amelyet eljáráshívással aktivizálunk. Mindkettőre látunk példát a bemutatott programokban. 1. Melyek a programozási tételek? A programozási tételek köre eredendően hat (6) egyszerű mintát tartalmaz. (Később kialakult a tételeknek egy haladó szintű köre is; ezzel most nem foglalkozunk.) A programozási tételek a legtöbb írásos forrásban (a sorrendtől függetlenül) a következők: 1. Számlálás Számoljuk meg, hogy az [m, n] intervallumban hány helyen teljesül a β feltétel! 2. Összegzés a.) Összegezzük az f függvény értékeit az [m, n] intervallum felett! (Ez így annyira egyszerű feladat, hogy magam nem is tekinteném programozási tételnek, hanem csak az itt következő b) feladatot és megoldását.) b.) Feltételes összegzés: összegezzük az f függvény értékeit az [m, n] intervallum azon pontjai felett, amelyekre teljesül a β tulajdonság! 3. Maximum kiválasztás Határozzuk meg az f függvény legnagyobb értékét a nem-üres [m, n] intervallum felett és adjuk meg azt a helyet is (ha több van, akkor az elsőt), ahol f ezt a legnagyobb értéket felveszi! 4. Feltételes maximum keresés Az előző feladathoz képest annyi az eltérés, hogy a maximum keresése csak azok a helyekre szorítkozik az [m, n] intervallumban, ahol a β tulajdonság fennáll. (Ha egyáltalán nincs ilyen elem, akkor ennek megfelelő választ kell adni.) 5. Szekvenciális (vagy lineáris) kiválasztás Keressük meg az első olyan helyet az m egésztől jobbra indulva, ahol a β tulajdonság fennáll ha tudjuk azt, hogy ilyen hely biztosan létezik! 2
6. Szekvenciális (vagy lineáris) keresés Keressük meg az [m, n] intervallumban az első olyan helyet, ahol a β tulajdonság teljesül, ha nem garantált ilyen hely létezése! (Ha nincs az intervallumban β tulajdonságú hely, akkor ilyen értelmű választ kell adnunk.) A történetileg kialakult elnevezéseket, változatosságukat nem kívánom elemezni, minősíteni, sőt teljeskörűen ismertetni sem. Az elnevezések olykor nem szerencsések, tapasztalható ugyan egy lassú korrekció, de szóhasználat java mára bevésődött. 2. A programozási tételek A programozási tételek forrásnak a [4] könyvet tekintem, amelyet ezennel idézek is. Az analóg módon történő programozás a programkészítés általánosan ismert és alkalmazott technikája. Amikor egy olyan feladatot kell megoldani, amelyhez hasonló feladatot már korábban megoldottunk, akkor az új feladat megoldását a korábbi minta alapján készítjük el. Analóg programozást többféleképpen is lehet végezni. Széles körben használják az analóg algoritmikus gondolkodást, amikor a minta megoldása során alkalmazott gondolatokat és döntéseket lemásolva készítjük el az új feladat megoldását. Kevésbé ismert technika a visszavezetés, amikor nem a mintafeladat megoldási folyamatát, hanem csak annak megoldó algoritmusát másoljuk le. Ehhez szükség van mind az új feladatnak, mind a mintafeladatnak a precíz leírására, a formális specifikációjára, hogy e kettőt összevetve pontosan felfedjük a két feladat közötti hasonlóságokat és eltéréseket. Egy új feladat visszavezetése során a vele hasonló mintafeladat megoldó algoritmusát (a minta algoritmust) sablonként használjuk, kicseréljük benne azokat az elemeket, amelyekben a kitűzött- és a mintafeladat specifikációja is eltér, hogy megkapjuk az új feladatot megoldó algoritmust. A programozási tételek következnek, de nem a fenti, hanem egy általam korábban használt sorrendben. (Nincs most időm átszerkeszteni és végig vezetni a korábbi anyagból kivett sorrendet; esetleg bejöhetne valamilyen hivatkozási ellentmondás is.) Az első két algoritmus érzékeny a ciklusszervezésre, a további négy már kevésbé, vagy egyáltalán nem. A lineáris kereséseknél figyelni arra, hogy miután megtaláltuk a keresett elemet, az azt indexelő ciklusváltozó értékét megnöveljük, így az nem a megtalált elemen áll. Biztosítani kell ezért, pl. egy ind változó bevezetésével, hogy a megtalált elemet ne veszítsük szem elől. (A tételek eredeti, Fóthi-féle formájában ez a jelenség nem lépett fel; erre még visszatérek.) 2.1. Kiválasztás (szekvenciális vagy lineáris kiválasztás) Feladat: Adott egy m egész szám és egy, az m helyen és attól jobbra értelmezett : Z L feltétel. Határozzuk meg az m helytől jobbra eső első olyan számot (ideértve magát az m helyet is), amely kielégíti a feltételt, ha tudjuk, hogy ilyen hely biztosan létezik! Specifikáció: A = (m: Z, ind: Z) Ef = (m=m i m: (i)) Uf = (Ef ind m (ind) i [m..ind 1]: (i) ) 3
Algoritmus: ind := m (ind) ind := ind + 1 Vegyük észre, hogy a programban nincs szükség egy külön i változóra az intervallum bejárásához, ezt a szerepet az ind eredményváltozó tölti be írja a szerző [4]. Saját kiegészítésem: ha a feltételt nem szeretnénk a ciklusfeltételbe beírni (pl. azért, mert több utasítással számítható), akkor egy logikai változó alkalmazásával a következő absztrakt programot kapjuk. Ez már egyaránt használja az i ciklusváltozót és az ind eredményváltozót. l, i := hamis, m l l, ind := (i), i i := i+1 2.2. Keresés (szekvenciális vagy lineáris keresés) Feladat: Adott az egész számok egy [m..n] intervalluma és egy : [m..n] L feltétel. Határozzuk meg az [m..n] intervallumban balról az első olyan számot, amely kielégíti a feltételt! Ha találunk ilyen elemet, akkor azt az l logikai változó igaz értéke jelzi, ind pedig megadja az elem indexét. Ha nincs az [m..n]-ben tulajdonságú elem, akkor ezt az l logikai változó hamis értéke mutatja és ekkor az ind változó értéke irreleváns. Specifikáció: Algoritmus: A = (m: Z, n: Z, l: L, ind: Z) Ef = (m=m n=n ) Uf = (Ef (l = i [m..n]: (i)) (l ind [m..n] (ind) i [m..ind 1]: (i)) ) l, i := hamis, m l i n l, ind := (i), i i := i+1 2.3. Számlálás (adott tulajdonságú elemek megszámolása) Feladat: Adott az egész számok egy [m..n] intervalluma és egy : [m..n] L feltétel. Határozzuk meg, hogy az [m..n] intervallumon a feltétel hányszor veszi fel az igaz értéket! (Ha m > n, akkor egyszer sem.) 4
Specifikáció: A = (m: Z, n: Z, c: N); Ef = (m=m n=n ); Uf = (Ef c = n i= m (i) 1 ) Algoritmus: c, i := 0, m i n (i) c := c+1 SKIP i := i + 1 2.4. Összegzés (feltétel nélküli, illetve adott tulajdonságú elemek összegzése) Feladat: Adott az egész számok egy [m..n] intervalluma és egy f: [m..n] H függvény. (A H halmaz elemein értelmezett összeadásnak és jelölje ezt a + jel.) Határozzuk meg az f függvény [m..n]-en felvett értékeinek az összegét! (Az m > n esetén, vagyis, ha üres az intervallum, akkor ennek az értéke nulla). Ez a feltétel nélküli összegzés. Specifikáció: Algoritmus: A = (m: Z, n: Z, s: H) Ef = ( m=m n=n ) Uf = ( Ef s = f (i) ) n i= m s, i := 0, m i n s : = s + f ( i) i := i + 1 A H halmaz helyén legtöbbször a valós számok R halmazával találkozunk. A bevezetőben mondottak szerint ezt az egyszerű, feltétel nélküli összegzést nem szívesen tartom külön programozási tételnek. A feltételes összegzést viszont igen. Ekkor az intervallum tulajdonságú elemein kell az f függvény értékeit összegezni. Algoritmus: s, i := 0, m i n s := s + f(i) (i) i := i + 1 SKIP 5
2.5. Maximum kiválasztás Feladat: Adott az egész számok egy [m..n] intervalluma és egy f: [m..n] H függvény. A H halmaz elemein értelmezett egy teljes rendezési reláció. Határozzuk meg, hogy az f függvény hol veszi fel az [m..n] nem üres (ez előfeltétel!) intervallumon a legnagyobb értéket, és mondjuk meg, hogy mekkora ez a maximális érték! Specifikáció: Algoritmus: A = (m: Z, n: Z, ind: Z, max: H) Ef = (m=m n=n n m) Uf = (Ef ind [m..n] max = f(ind) i [m..n]: max f(i)) max, ind, i := f(m), m, m+1 i n max < f(i) max, ind := f(i), i SKIP i := i + 1 Találkozhatunk olyan feladatokkal, ahol csak az ind, vagy csak a max értékére van szükség. A második esetben az ind-nek történő értékadás törölhető, de az első esetben a max változóra mindenképpen szükség van. Érdemes megjegyezni, hogy ez az a programozási tétel, amelyet az algoritmusok és adatstruktúrák elnevezésű diszciplína ennek emblematikus műve az [5] könyv felvesz a nyilvántartott és tanulmányozott algoritmusok sorába. 2.6. Feltételes maximumkeresés Feladat: Adott az egész számok egy [m..n] intervalluma, egy f: [m..n] H függvény és egy : [m..n] L feltétel. A H halmaz elemein értelmezett egy teljes rendezési reláció. Határozzuk meg, hogy az [m..n] intervallum feltételt kielégítő elemei közül az f függvény hol veszi fel a legnagyobb értéket, és mondjuk meg, mekkora ez az érték! Lehet, hogy egyáltalán nincs feltételt kielégítő elem az [m..n] intervallumban, vagy m > n. Ekkor az l logikai változó hamis értéke jelezze ezt, és ekkor a max és ind változók értéke irreleváns. A pozitív esetet pedig l változó igaz értéke jelezze! Specifikáció: A = (m: Z, n: Z, l: L, ind: Z, max: H) Ef = (m=m n=n ) Uf = (Ef (l = i [m..n]: (i) ) ( l ind [m..n] max = f(ind) (ind) ( i [m..n]: (i) max f(i)))) 6
Algoritmus: l, i := hamis, m i n (i) l (i) l (i) SKIP max < f(i) l, max, ind := max, ind := f(i), i i := i + 1 SKIP igaz, f(i), i Az elágazás harmadik ága igényelhet egy kevés magyarázatot. Az l logikai változó jelentése az, hogy találtunk-e már tulajdonságú elemet az intervallum bejárása során. Kezdetben még nyilván: nem. Amikor az első ilyen elemre lépünk, akkor kerül a vezérlés a harmadik ágra. Itt l-et át kell állítani igazra, és inicializálni kell a max és az ind változókat, éppen úgy, mint a maximum kiválasztás első lépésében. 3. Az algoritmusok leírási módja Az algoritmusok megadásának módja az ELTE IK-n hagyományosan a struktogram forma. A grafikus kifejező erővel rendelkező leírási módot jobban szeretik a hallgatók, még a matematikusok is előnyben részesítik, noha először pszeudokóddal találkoznak. Magam is ezen a véleményen vagyok. Ugyanakkor legalább is a matematikusoknál nálam, a tantárgyak elvégzése során szabadon lehet pszeudokódot is használni, ha valaki azt kedveli. Akár struktogram, akár pszeudokód a választott eszköz, azon belül egyaránt minimalista jelölésmódot használnak az ELTE-n. Ha ezt a készletet összehasonlítjuk meghatározó jelentőségű [5] könyvben alkalmazott leírási móddal, akkor rögtön szembe tűnik, hogy abban a szerzők bővebb készlettel dolgoznak. - Nálunk, a [4]-ben a struktogram nem függvény, nem alprogram, hanem csak egy absztrakt utasításokból álló vezérlési struktúra, amely megoldja a feladatot. Az [5] szerzői az algoritmusokat (meghívható) absztrakt függvények formájában adják meg, amelyekből a vezérlés az eredményeket visszaadó return utasítás hatására vissza. - A [4]-ben szereplő programozási tételeknél nem találunk fejléc sort ; ilyen csak akkor szerepel az ELTE-s eszköztárban, ha egy absztrakt algoritmus felhasználásra (úgymond meghívásra ) kerül. Az [5] könyvben a függvényeket fejléc sor vezeti be, amely a függvény nevét és input paramétereit tartalmazza. - Míg a [4] mindig saját szervezésű while-ciklust használ, addig az [5]-ben alkalmazzák a for-ciklust is, ha egy keresési tartományon egyszerűen csak végig kell menni. - A [4]-ben leírt ELTE-s felfogás szerint a vezérlés mindig a struktogram alján ér véget. Az [5] könyv megengedi azt, hogy a return utasítás bármely ponton elhagyja a függvény eljárást és output értékkekkel visszatérjen a hívás helyére. 7
- Az előző különbség már eléggé lényeginek mondható. Ennek következménye az alábbi eltérés a ciklusfeltételek kialakításában. A [4]-ben a ciklusfeltételek gyakran összetettebbek, mert logikai kifejezésekkel pontosan meg kell adni a ciklus terminálásának feltételét. Az [5] könyvben szereplő bonyolultabb szervezésű ciklusok feltételei csak azt adják meg, hogy legfeljebb meddig fut az iteráció és a ciklus belsejében elhelyezett return utasítások gondoskodnak az iteráció befejezéséről, igaz, hogy csak abban az esetben, ha ez a függvény eljárást elhagyását (és a vezérlés visszatérését) is jelent egyben. - Az [4]-es ELTE-s feladatmegoldások általában kisebb egységekre tagoltabbak, abból következően, hogy egy-egy alkalmazott programozási tétel megmarad külön egységnek. Az idézett [5] könyvben viszont gyakran beintegrálódik egy alkalmazott programozási tétel a felhasználó eljárásba; szinte nem is tudjuk már azonosítani. (Túl nagy méretű függvény-eljárások ott sem szerepelnek.) Világos, hogy a puritánabb ELTE-s változatra könnyebb kijelenteni, hogy semmilyen implementálási utasítást nem szeretnénk adni, de még sugallani sem, egy feladat absztrakt megoldásában. Az is világos azonban, hogy ha programot kell írni, akkor a mi informatikus hallgatóinknak is el kell jutniuk a programkódhoz, amelyhez a CLRS-féle felfogás közelebb áll meg absztrakt szinten. Ahogyan említettem saját gyakorlatomat, talán célszerű egy ideig két lépésben is megadni az absztrakt megoldást: először a [4]-ben leírt eszköztárral, majd meghozni az [5]-nek megfelelő döntéseket. Véleményem szerint egyébként is eleve, tehát egy megelőző mélyebb, absztraktabb szintű megoldás nélkül is megengedhető az [5]-ben alkalmazott elemek használata. Aki először az [5] stílusú algoritmus-leírással találkozik, az sincs hátrányban velünk szemben. Személyesen azonban örülök, hogy ezzel a szakmai kultúrával találkoztam. Megállapítható, hogy az informatikusok mégis csak érzékenyek arra, hogy a végső absztrakt megoldásuk ne legyen túlságosan távol az implementációtól, a programnyelvek világától. Nézzük sorban az [5]-ben látott vezérlési szerkezeteket: - A for-ciklus használata nem módosítja a megoldás absztrakt szintjét, továbbá kifejezőbb, mint egy while-ciklussal, ha egy tartomány bejárása a feladat. - A fejléc sort úgy lehet tekinteni, mint az eljárás szignatúráját, amely az input és output paramétereket mintegy hangsúlyosan kiemeli a specifikációból. Ha csak az inputot tartalmazza a fejléc, akkor tudatosítjuk, hogy melyek a visszaadott eredmények. - A return utasítás kétségtelenül utal arra, hogy implementáljunk függvényt, de csak utal rá; határozott utasítást biztosan nem ad. - A return utasítás a strukturált programozás követői által is elfogadott módon, legálisan elhagyja azt a vezérlési struktúrát (legtöbbször ciklust), amelyben szerepel. Ezáltal általában mentesíti a programozót az összetett (logikai változóval konjugált) ciklusfeltétel írásától. (Az a realitás, hogy a legtöbb programozó már nem is hajlandó összetett ciklusfeltételt írni, ha megoldható egy alkalmas return -nel a ciklus elhagyása.) 8
(Itt most áttérek pszeudokódra, aminek nincs különösebb jelentősége, csak annyi, hogy ez a leírás így hamarább készül el. A fenti rajzokat az [4] könyv kéziratából vettem át, itt magam nem készítettem ábrát a jegyzetemben igen, de máshogyan, most jelentős időbe telne az új ábrák elkészítése.) Például, a maximum kiválasztás tételét így is meg lehet fogalmazni (a SKIP-ág elhagyásával): MaxKiv (m, n) 1 max, ind := f(m), m 2 for i = m+1 to n 3 if max < f(i) 4 then max, ind := f(i), i 5 return max, ind A szekvenciális keresés (korábban: lineáris keresés 2) programozási tételét is felírjuk fejsorral és return utasítás alkalmazásával. Lehetne for-ciklust alkalmazni és azt elhagyni return-t, de ehhez stílusában jobban illik a while-ciklus. A találat tényét (igaz vagy hamis) nem logikai változó közvetíti, hanem a visszaadott érték: ha m <= i <= n, akkor találtunk béta, azaz (i) tulajdonságú helyet, akkor éppen az i hely az első ilyen; ha viszont az intervallumon kívüli n+1 értéket adjuk vissza, az azt jelenti, hogy az intervallum nem tartalmaz béta ( ) tulajdonságú egész helyet. (Általános szemlélet: a keresés negatív eredményét a keresés helyének egy nem-reális, extremális értéke mutatja.) LinKer2 (m, n) 1 i := m 2 while i <= n 3 if béta (i) 4 then return i 5 else i := i+1 6 return n+1 A szekvenciális keresés leírási módja lényegében olyan, mint amilyen absztrakt kódokat a [5] könyvben láthatunk. Felmerülhet a kérdés, hogy (függetlenül attól, hogy struktogramot vagy pszeudokódot használunk) melyik a jobb: az eredeti puritán változat, vagy ez a bővebb eszköztárú stílus? Melyik formában jegyezzük meg a programozási tételeket? Ezt szívesen az egyénre (hallgatóra, olvasóra) bíznám, azzal a javaslattal, hogy bármit is választ, a leírást tekintse absztrakt algoritmusnak, amely nem tartalmaz kódolási előírást. Magam egy olyan elég konzervatív leírási módot használok, amely közel áll az eredetihez: struktogram, fejléc sorral kiegészítve, for-ciklust is megengedve, de logikai változók használatával a nem-fix lefutású ciklusok feltételében, a return utasítást mellőzve. 9
Ezt követően ahogy fentebb jeleztem még következik az absztrakt függvény [5]-stílusú kialakítása. Ennek megfelelően a programkód (jelenleg a Python nyelven) általában jelentős formai különbségeket mutat az eredeti absztrakt megoldáshoz képest, ugyanis (1) a kódban megjelenik a return utasítás, továbbá (2) a tételben szereplő logikai változók a kódban már gyakran nem szerepelnek, mert (a) a return folytán egyszerűbb lesz a ciklus-szervezés és (b) a negatív választ nem logikai változó, hanem extremális visszaadott érték közvetíti. Ez az algoritmus leírási formát tartom a legkifejezőbbnek absztrakt szinten. Természetesen semmi nehézséget nem jelent a modernebb változat használata sem, különös tekintettel a gyakran forgatott [5] könyv algoritmusaira. 4. A programozási tételek alkalmazása és kódolása Ha egy feladat megoldásában felismerjük egy vagy több programozási tétel alkalmazást, az biztonságot ad az absztrakt megoldás létrehozásában, a programtervezésben és a kódolásban. Ez akkor is igaz, ha végül a programkódban az alkalmazott programozási tétel eredeti alakja megváltozik. Az absztrakt megoldás implementálása, a programkód létrehozása önálló kreatív lépés a számítógépes megoldási folyamatban. Számos példaprogramot mellékelek, így hiteles! Egy programozási tétel formai szempontból alapvetően két módon válhat a programkód alkotó részévé: (1) Gyakoribb az az eset, amikor a szükséges módosításokkal (pl. a béta ( ) tulajdonság konkrét megadásával) a tételt mintegy beszerkesztjük a programkódba. Ekkor a fejsor nyilván nem szerepel a kódban. Ebben az esetben inkább olyan változatot használunk, amelyik nem tartalmaz return utasítást, mivel az egy önálló függvény megírására utal. Előfordul azonban olyan eset (mint például a prím-tulajdonságot eldöntő feladatnál), hogy a tételt befogadó eljárásból éppen a programozási tétel return utasítása jelenti a helyes kilépést. Ilyen esetben a return utasítást tartalmazó változatot be tudjuk integrálni egy eljárás kódjába. (2) Ritkább esetben a programozási tételt önálló függvényként írjuk meg. Ebben az esetben a return utasítást tartalmazó változatot vehetjük alapul. Ekkor a fejsor a függvény definíció első sora lesz. Példát is látunk majd erre a Dijkstra algoritmus kódolása esetén, ahol a feltételes maximumkeresés tételét célszerű külön függvényként átvenni (némi átalakítás mellett). Irodalom [1] E. W. Dijkstra: A Discipline of Programming. Prentice-Hall, Englewood Cliffs, 1973 [2] Fóthi Á.: Bevezetés a programozáshoz. ELTE jegyzet, 1985 [3] Fóthi Á.: Bevezetés a programozáshoz. ELTE Eötvös Kiadó, 2005 [4] Gregorics T.: Programozás, 1. kötet: Tervezés, 2. kötet: Megvalósítás. ELTE Eötvös Kiadó, 2013 [5] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein: Új algoritmusok, Scolar Kiadó, 2003 10