\documentclass[margin=0px]{article} \usepackage{listings} \usepackage[utf8]{inputenc} \usepackage{graphicx} \usepackage{float} \usepackage[a4paper, margin=0.7in]{geometry} \usepackage{amsthm} \usepackage{amssymb} \usepackage{amsmath} \usepackage{fancyhdr} \usepackage{pdfpages} \usepackage{setspace} \onehalfspacing \newenvironment{tetel}[1]{\paragraph{#1 \\}}{} \renewcommand{\figurename}{ábra} \makeatletter \renewcommand\paragraph{% \@startsection{paragraph}{4}{0mm}% {-\baselineskip}% {.5\baselineskip}% {\normalfont\normalsize\bfseries}} \makeatother \pagestyle{fancy} \lhead{\it{PTI BSc Záróvizsga tételek}} \rhead{7. Programozás} \title{\textbf{{\Large ELTE IK - Programtervező Informatikus BSc} \vspace{0.2cm} \\ {\huge Záróvizsga tételek}} \vspace{0.3cm} \\ 7. Programozás} \author{} \date{} \begin{document} \maketitle \begin{tetel}{Programozás} A felsoroló fogalma. Nevezetes gyűjtemények (intervallum, tömb, sorozat, szekvenciális inputfájl) felsorolói. Felsorolóra megfogalmazott programozási tételek (összegzés, számlálás, maximum kiválasztás, feltételes maximumkeresés, lineáris keresés, kiválasztás). A visszavezetés módszere. Programozási tételekkel készült programok tesztelése. \end{tetel} \section{Egyszerű programozási feladat megoldásának lépései} \subsection{Bevezetés} Egy programozási feladat megoldása a kódoláson túl jó néhány tevékenységet tartalmaz. Az első teendő a feladat pontos meghatározása, a specifikáció. Ez a feladat szöveges és formalizált, matematikai leírásán (a specifikáció ún. szűkebb értelmezésén) túl tartalmazza a megoldással szemben támasztott követelményeket, környezeti igényeket is (ami a specifikáció ún. tágabb értelmezése). A specifikáció alapján meg lehet tervezni a programot, elkészülhet a megoldás algoritmusa és az algoritmus által használt adatok leírása. Az algoritmus és az adatszerkezet finomítása egymással párhuzamosan halad, egészen addig a szintig, amelyet a programozó ismeretei alapján már könnyen, hibamentesen képes kódolni. Gyakran előfordul, hogy a tervezés során derül fény a specifikáció hiányosságaira, így itt visszalépésekre számíthatunk. Az algoritmusírás után következhet a kódolás. Ha a feladat kitűzője nem rögzítette, akkor ez előtt választhatunk a megoldáshoz programozási nyelvet. A kódolás eredménye a programozási nyelven leírt program. A program első változatban általában sohasem hibátlan, a helyességéről csak akkor beszélhetünk, ha meggyőződtünk róla. A helyesség vizsgálatának egyik lehetséges módszere a tesztelés. Ennek során próbaadatokkal próbáljuk ki a programot, s az ezekre adott eredményből következtetünk a helyességre. (Ne legyenek illúzióink afelől, hogy teszteléssel eldönthető egy program helyessége. Hisz hogy valójában helyes-e a program – sajnos – nem következik abból, hogy nem találtunk hibát.) Ha a tesztelés során hibajelenséggel találkozunk, akkor következhet a hibakeresés, a hibajelenséget okozó utasítás megtalálása, majd pedig a hibajavítás. A hiba kijavítása több fázisba is visszanyúlhat. Elképzelhető, hogy kódolási hibát kell javítanunk, de az is lehet, hogy a hibát már a tervezésnél követtük el. Javítás után újra tesztelni kell, hiszen – legyünk őszinték magunkhoz!– nem kizárt, hogy hibásan javítunk, illetőleg – enyhe optimizmussal állítjuk:– a javítás újabb hibákat fed fel, ... E folyamat végeredménye a helyes program. Ezzel azonban még korántsem fejeződik be a programkészítés. Most következnek a minőségi követelmények. Egyrészt a hatékonyságot kell vizsgálnunk (végrehajtási idő, helyfoglalás), másrészt a kényelmes használhatóságot. Itt újra visszaléphetünk a kódolási, illetve a tervezési fázisba is. Ezzel elérkeztünk a jó programhoz. \subsection{Specifikáció} A programkészítés menetének első lépése a feladat meghatározása, precíz "újrafogalmazása". Milyen is legyen, mit várjunk el tőle? Nézzünk meg néhány – jónak tűnő – követelményt egyelőre címszavakban! (A továbbiakban a specifikáció szűkebb értelmezéséről lesz szó.) A specifikáció legyen: \begin{itemize} \item helyes, egyértelmű, pontos, teljes \item rövid, tömör, ami legegyszerűbben úgy érhető el, hogy ismert formalizmusokra építjük \item szemléletes, érthető (amit időnként nehezít a formalizáltság) \end{itemize} A specifikáció első közelítésben lehetne a feladatok szövege. Ez azonban több problémát vethet fel: \begin{itemize} \item mi alapján adjuk meg a megoldást \item mit is kell pontosan megadni? \end{itemize} Például az a feladat, hogy adjuk meg N ember közül a legmagasabbat. A legmagasabb ember megadása mit jelent? Adjuk meg a sorszámát, vagy a nevét, vagy a személyi számát, vagy a magasságát, esetleg ezek közül mindegyiket? Tanulságként megállapíthatjuk, hogy a specifikációnak tartalmaznia kell a bemenő és a kimenő adatok leírását. \begin{verbatim} Bemenet: N : az emberek száma, A : a magasságukat tartalmazó sorozat. Kimenet: MAX : a legmagasabb ember sorszáma. \end{verbatim} Tudjuk-e, hogy a bemenő, illetve a kimenő változók milyen értéket vehetnek fel? Például az emberek magasságát milyen mértékegységben kell megadni? Az eredményül kapott sorszám milyen érték lehet: 1-től sorszámozunk, vagy 0-tól? Megállapíthatjuk tehát, hogy a specifikációban a bemeneti és a kimeneti változók értékhalmazát is meg kell adnunk. \begin{verbatim} Bemenet: N : az emberek száma, természetes szám; A : a magasságukat tartalmazó sorozat, egész számok, amelyek a magasságot centiméterben fejezik ki (a sorozatot 1-től N-ig indexeljük). Kimenet: MAX : a legmagasabb ember sorszáma, 1 és N közötti természetes szám. \end{verbatim} Most már a bemenő és a kimenő változók értékhalmazát pontosan meghatároztuk, csupán az a probléma, hogy a feladatban használt fogalmakat és az eredmények kiszámítási szabályát nem definiáltuk. A specifikációnak tehát tartalmaznia kell a feladatban használt fogalmak definícióját, valamint az eredmény kiszámítási szabályát. Itt lehetne megadni a bemenő adatokra vonatkozó összefüggéseket is. A bemenő, illetve a kimenő adatokra kirótt feltételeket nevezzük előfeltételnek, illetve utófeltételnek. Az előfeltétel nagyon sokszor egy azonosan igaz állítás, azaz a bemenő adatok értékhalmazát semmilyen "külön” feltétellel nem szorítjuk meg. \begin{verbatim} Bemenet: N : az emberek száma, természetes szám, A : a magasságukat tartalmazó sorozat, egész számok, amelyek a magasságot centiméterben tartalmazzák (a sorozatot 1-tol N-ig indexeljük). Kimenet: MAX : a legmagasabb ember sorszáma, 1 és N közötti természetes szám. Elofeltétel: A[i]-k pozitívak. Utófeltétel: MAX olyan 1 és N közötti szám, amelyre A[MAX] nagyobb vagy egyenlo, mint a sorozat bármely eleme (az 1. és az N. között). \end{verbatim} Újabb probléma merülhet fel bármelyik feladattal kapcsolatban: az eddigiek alapján a "várttól” lényegesen különböző – nyugodtan állíthatjuk: "banális” –, az elő- és utófeltételnek megfelelő megoldást is tudunk készíteni. Itt persze arról a hallgatólagos (tehát még meg nem fogalmazott, ki nem mondott) feltételezésről van szó, hogy a bemeneti változók értéke nem változik meg. Ez sajnos nem feltétlenül igaz. A probléma megoldására kétféle utat követhetünk (a későbbiekben mindkettőt alkalmazni fogjuk): \begin{itemize} \item az utófeltételbe automatikusan beleértjük, hogy "és a bemeneti változók értéke nem változik meg”, s külön kiemeljük, ha mégsem így van; \item az elő- és az utófeltételt a program paramétereire fogalmazzuk meg, amelyeket formailag megkülönböztetünk a program változóitól, és emiatt nem a paraméterek fognak változni, hanem a programbeli változók (ebben az esetben természetesen az elő- és az utófeltételben meg kell fogalmazni a paraméterek és a megfelelő programbeli változók értékének azonosságát). \end{itemize} A második megoldásból az következik, hogy meg kell különböztetnünk egymástól a feladat és a program elő–, illetve utófeltételét! Ez hosszadalmasabbá – bár precízebbé – teszi a feladat megfogalmazását, emiatt ritkábban fogjuk alkalmazni. Előfordulhat, hogy a feladat megfogalmazása alapján nem lehet egyértelműen meghatározni az eredményt, ugyanis az utófeltételnek megfelelő több megoldás is létezik. Ez a jelenség a feladat ún. nemdeterminisztikussága. Ehhez a nemdeterminisztikus feladathoz tehát determinisztikus programot kell írnunk, aminek az utófeltétele már nem engedheti meg a nem egyértelműséget, a nemdeterminisztikusságot. E probléma miatt tehát mindenképpen meg kell különböztetnünk egymástól a feladat és a program elő–, illetve utófeltételét! \begin{verbatim} Bemenet: N : az emberek száma, természetes szám, A : a magasságukat tartalmazó sorozat, egész számok, amelyek a magasságot centiméterben tartalmazzák (a sorozatot 1-tol N-ig indexeljük). Kimenet: MAX : a legmagasabb ember sorszáma, 1 és N közötti természetes szám. Elofeltétel: A[i]-k pozitívak. Utófeltétel: MAX olyan 1 és N közötti szám, amelyre A[MAX] nagyobb vagy egyenlo, mint a sorozat bármely eleme (az 1. és az N. között). Program utófeltétel: MAX olyan 1 és N közötti szám, amelyre A[MAX] nagyobb vagy egyenlo, mint a sorozat bármely eleme (az 1. és az N. között) és elotte nincs vele egyenlo. \end{verbatim} Megállapíthatjuk ebből, hogy a program utófeltétele lehet szigorúbb, mint a feladaté, emellett az előfeltétele pedig lehet gyengébb. Visszatekintve a specifikáció eddig "bejárt pályájára” egy szemléletes modellje körvonalazódik a feladatmegoldásnak. Nevezetesen: nyugodtan mondhatjuk azt, hogy a feladatot megoldó program egy olyan automatát határoz meg, amelynek pillanatnyi állapota a feladat paraméterei (a program változói) által "kifeszített” halmaz egy eleme. (E halmaz annyi dimenziós, ahány paraméterváltozója van a programnak; minden dimenzió egyik változó értékhalmaza. Tehát egy konkrét időpillanatban e "gép” állapota: a változóinak abban a pillanatban érvényes értékeinek együttese.) Ezt a halmazt nevezzük a program állapotterének. Amikor megfogalmazzuk az előfeltételt, akkor tulajdonképpen kihasítjuk ebből az állapottérből azt a részt (azt az altért), amelyből indítva elvárhatjuk az automatánktól (amit a megoldó program vezérel), hogy a helyes eredményt előállítja egy végállapotában. A végállapotot jelöltük ki az utófeltétellel. Ezt a modellt elfogadva adódik még egy további megoldásra váró kérdés. Akkor ugyanis, amikor a programot írjuk, lépten-nyomon a részeredmények tárolására újabb és újabb változókat vezetünk be. Fölvetődik a kérdés: hogyan egyeztethető össze az imént elképzelt modellel? A válasz egyszerű: minden egyes újabb változó egy újabb dimenziót illeszt az eddig létrejött állapottérhez. Tehát a programozás folyamata – leegyszerűsítve a dolgot – nem áll másból, mint annak pontosításából, hogy hogyan is nézzen ki a megoldó automata állapottere (és persze: hogyan kell az egyik állapotból a másik állapotba jutnia). A feladatban szereplő paraméterek meghatározta "embrionális” állapotteret hívhatjuk paramétertérnek, ami csak altere a program valódi állapotterének. Ez is azt sugallja, hogy a feladat előfeltétele gyengébb (azaz az általa kijelölt állapothalmaz) lehet, mint a program előfeltétele. Foglaljuk most össze, hogy melyek a specifikáció részei! Ezek az eddigiek, valamint a programra vonatkozó további megkötések lesznek. \begin{enumerate} \item A feladat specifikálása \begin{itemize} \item a feladat szövege, \item a bemenő és a kimenő adatok elnevezése, értékhalmazának leírása, \item a feladat szövegében használt fogalmak definíciói (a fogalmak fölhasználásával), \item a bemenő adatokra felírt előfeltétel (a fogalmak fölhasználásával), \item a kimenő adatokra felírt utófeltétel. \end{itemize} \item A program specifikálása \begin{itemize} \item a bemenő és a kimenő adatok elnevezése, értékhalmazának leírása, \item (a feladat elő-, illetve utófeltételétől esetleg különböző) program elő- és utófeltétel, \item a feladat megfogalmazásában használt fogalmak definíciói. \end{itemize} \end{enumerate} \noindent Ezek az absztrakt specifikáció elemei. Az alábbiak másodlagos, mondhatjuk: technikai specifikáció részei: \begin{itemize} \item a program környezetének leírása (számítógép, memória- és perifériaigény, programozási nyelv, szükséges fájlok stb.), \item a programmal szembeni egyéb követelmények (minőség, hatékonyság, hordozhatóság stb.). \end{itemize} \noindent A technikai specifikáció nélküli leírást a program szűkebb specifikációjának nevezik.\\ \noindent Progos specifikáció:\\\\ $A=(N : \mathbb{N}, A: \mathbb{N}^{1..N})$\\ $Ef=(\forall i \in {1..N} : A_{i}>0)$\\ $Uf=(Ef \land \forall i \in {1..N} : A_{MAX}>=A_{i} \land \forall j \in {1..MAX-1} : A_{i})$ helyett pl. $(\mathbb{Z},>)$ vagy $(\mathbb{Z},<)$ \item $(H,+)$ helyett pl. $(\mathbb{Z},+)$ vagy $(\mathbb{R},*)$ \end{itemize} \item a változók átnevezése \end{itemize} \item A különbségek figyelembe vételével a tétel algoritmusából elkészítjük a konkrét feladatot megoldó algoritmust. \end{enumerate} \section{Felsoroló, a felsoroló típus specifikációja} A gyűjtemény (tároló, kollekció, iterált) egy olyan adat (objektum), amely valamilyen elemek tárolására alkalmas. \begin{itemize} \item Ilyenek az összetett szerkezetű, de különösen az iterált szerkezetű típusok értékei: halmaz, sorozat (verem, sor, fájl), fa, gráf \item De vannak úgynevezett virtuális gyűjtemények is: pl. egész számok egy intervallumának elemei, vagy egy természetes szám prím-osztói \end{itemize} \noindent Egy gyűjtemény feldolgozásán a benne levő elemek feldolgozását értjük. \begin{itemize} \item Keressük a halmaz legnagyobb elemét! \item Hány negatív szám van egy számsorozatban? \item Válogassuk ki egy fa leveleiben elhelyezett értékeket! \item Járjuk be az [m .. n] intervallum minden második elemét visszafelé! \item Adjuk össze az n természetes szám prím-osztóit! \end{itemize} \noindent A feldolgozni kívánt elemek felsorolását (bejárását) az alábbi műveletekkel szabványosítjuk: \begin{itemize} \item First() : Rááll a felsorolás első elemére, azaz elkezdi a felsorolást \item Next() : Rááll az elkezdett felsorolás soron következő elemére \item End() : Mutatja, ha a felsorolás végére értünk \item Current() : Visszaadja a felsorolás aktuális elemét \end{itemize} Egy felsorolásnak különböző állapotai vannak (indulásra kész, folyamatban van, befejeződött), és a műveletek csak bizonyos állapotokban értelmezhetők (máshol a hatásuk nem definiált). A feldolgozó algoritmus garantálja, hogy a felsoroló műveletek mindig megfelelő állapotban kerüljenek végrehajtásra. \begin{figure}[H] \centering \includegraphics[width=0.4\linewidth]{img/felsorolas_stuki} \caption{A felsorolás algoritmusa} \label{fig:felsorolas_stuki} \end{figure} A felsorolást sohasem a felsorolni kívánt gyűjtemény, hanem egy külön felsoroló objektum végzi. \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_speci} \caption{A felsoroló objektum és típusa} \label{fig:felsorolas_stuki} \end{figure} \section{Felsorolóra megfogalmazott programozási tételek} \includepdf[pages={1-2}]{progtetel_felsorolo.pdf} \section{Nevezetes gyűjtemények felsorolói} \subsection{Intervallum} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_intervallum} \caption{Intervallum felsorolója} \label{fig:felsorolo_intervallum} \end{figure} \subsection{Tömb} Itt két különböző tömbtípus felsorolóját mutatjuk be: az egydimenziós (vektor) és a kétdimenziós tömbét (mátrix). \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_vektor} \caption{Vektor felsorolója} \label{fig:felsorolo_vektor} \end{figure} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_matrix} \caption{Mátrix sorfolytonos felsorolója} \label{fig:felsorolo_matrix} \end{figure} Megjegyzés: a felsorolás történhet másképpen is, például vektor esetén végezhetjük a felsorolást visszafelé, a tömb végétől kezdve, vagy mátrixnál alkalmazhatunk pl. oszlopfolytonos bejárást. \subsection{Sorozat} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_sorozat} \caption{Sorozat felsorolója} \label{fig:felsorolo_sorozat} \end{figure} \subsection{Halmaz} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_halmaz} \caption{Halmaz felsorolója} \label{fig:felsorolo_halmaz} \end{figure} \subsection{Szekvenciális inputfájl} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/felsorolo_seqin} \caption{Szekvenciális inputfájl felsorolója} \label{fig:felsorolo_seqin} \end{figure} \section{Programozási tételekkel készült programok tesztelése} Három féle tesztelési stratégia van, ezek az alábbiak. \begin {enumerate} \item Fekete doboz: a feladat (specifikációja) alapján felírt tesztesetek. \begin {enumerate} \item Az előfeltételt kielégítő (érvényes), illetve azt megszegő (érvénytelen) tesztadatokkal felírt tesztesetek. \item Az utófeltétel alapján (?) generált tesztesetek vizsgálata. \end {enumerate} \item Fehér doboz: a kód alapján felírt tesztesetek. \begin {enumerate} \item Algoritmus minden utasításának kipróbálása \item Algoritmus minden vezérlési csomópontjának (elágazás, ciklus) kipróbálása \end {enumerate} \item Szürke doboz: végrehajtható specifikáció által előrevetített algoritmus működését ellenőrző tesztesetek. \begin {enumerate} \item Ha a végrehajtható specifikáció ráadásul egy algoritmus-mintából származik, akkor az algoritmus-minta szokásos teszteseteit kell megvizsgálni. \end {enumerate} \end {enumerate} A programozási tételek algoritmus-minták, ezért itt azok teszteseteivel fogunk foglalkozni. \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/algoritmus-mintak_tesztesetei} \caption{Algoritmus-minták tesztesetei} \label{fig:algoritmus-mintak_tesztesetei} \end{figure} Példafeladat programozási tételre (programozási-mintára) építve: \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/programozasi_tetelre_epulo_feladat} \caption{Feladat programozási tételre építve} \label{fig:programozasi_tetelre_epulo_feladat} \end{figure} \begin{figure}[H] \centering \includegraphics[width=0.7\linewidth]{img/tesztesetek} \caption{Példa tesztesetek} \label{fig:tesztesetek} \end{figure} \end{document}