mirror of
https://github.com/TMD44/elte-ik-pti-bsc-zarovizsga.git
synced 2025-08-11 21:39:05 +02:00
633 lines
36 KiB
TeX
633 lines
36 KiB
TeX
\documentclass[12pt,margin=0px]{article}
|
||
|
||
\usepackage[a4paper, margin=1in]{geometry}
|
||
\usepackage[normalem]{ulem}
|
||
\usepackage[table,xcdraw]{xcolor}
|
||
\usepackage[thinlines]{easytable}
|
||
\usepackage[utf8]{inputenc}
|
||
\usepackage[hungarian]{babel}
|
||
\usepackage{amsmath}
|
||
\usepackage{amssymb}
|
||
\usepackage{amsthm}
|
||
\usepackage{enumitem}
|
||
\usepackage{fancyhdr}
|
||
\usepackage{float}
|
||
\usepackage{graphicx}
|
||
\usepackage{hhline}
|
||
\usepackage{listings}
|
||
\usepackage{makecell}
|
||
\usepackage{multirow}
|
||
\usepackage{pdfpages}
|
||
\usepackage{subcaption}
|
||
\usepackage{xcolor}
|
||
\usepackage{verbatim}
|
||
\usepackage{dashrule}
|
||
|
||
\definecolor{mygray}{rgb}{0.15, 0.15, 0.15}
|
||
|
||
\setlist[itemize,1]{label=$\bullet$}
|
||
\setlist[itemize,2]{label=$\circ$}
|
||
\setlist[itemize,3]{label=$\centerdot$}
|
||
\setlist[itemize,4]{label=$\cdot$}
|
||
|
||
\pagestyle{fancy}
|
||
|
||
\newcommand\blfootnote[1]{%
|
||
\begingroup
|
||
\renewcommand\thefootnote{}\footnote{#1}%
|
||
\addtocounter{footnote}{-1}%
|
||
\endgroup
|
||
}
|
||
|
||
\renewcommand{\figurename}{ábra}
|
||
\newenvironment{tetel}[1]{\paragraph{#1 \\}}{}
|
||
\renewcommand{\baselinestretch}{1.15}
|
||
|
||
\newcommand{\N}{\mathbb{N}}
|
||
\newcommand{\Z}{\mathbb{Z}}
|
||
\newcommand{\R}{\mathbb{R}}
|
||
\newcommand{\Q}{\mathbb{Q}}
|
||
\newcommand{\C}{\mathbb{C}}
|
||
|
||
\makeatletter
|
||
\renewcommand\paragraph{%
|
||
\@startsection{paragraph}{4}{0mm}%
|
||
{-\baselineskip}%
|
||
{.5\baselineskip}%
|
||
{\normalfont\normalsize\bfseries}}
|
||
\makeatother
|
||
|
||
% A dokument itt kezdődik
|
||
|
||
\useunder{\uline}{\ul}{}
|
||
\fancyhead{}
|
||
\cfoot{7. tétel | \thepage. oldal}
|
||
|
||
\renewcommand{\headrulewidth}{0pt}
|
||
\renewcommand{\footrulewidth}{0.4pt}
|
||
|
||
\begin{document}
|
||
\thispagestyle{fancy}
|
||
\hyphenation{oddword}
|
||
\uchyph=0
|
||
|
||
{\Large\bfseries\noindent 7. Programozás} \\
|
||
|
||
\section*{Egyszerű programozási feladat megoldásának lépései}
|
||
\subsection*{Bevezetés}
|
||
Egy programozási feladat megoldása nem csupán a kódolásból áll. Tartalmaz még jó néhány egyéb tevékenységet.\\
|
||
|
||
\noindent Az első teendő a feladat pontos meghatározása, a \emph{specifikáció}, ami tartalmazza:
|
||
\begin{itemize}
|
||
\item[-] A feladat szöveges és formalizált, matematikai leírását (a specifikáció ún. szűkebb értelmezésén)
|
||
\item[-] A megoldással szemben támasztott követelményeket, környezeti igényeket (ami a specifikáció ún. tágabb értelmezése).
|
||
\end{itemize}
|
||
|
||
\noindent A specifikáció alapján meg lehet tervezni a programot, ahol elkészülhet
|
||
\begin{itemize}
|
||
\item[-] a \emph{megoldás algoritmusa}, és az
|
||
\item[-] algoritmus által használt \emph{adatok leírása}.
|
||
\end{itemize}
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent Az algoritmusírás után következhet a \emph{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.\\
|
||
|
||
\noindent 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 \emph{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.\\
|
||
\emph{Megyjegyzés: (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.)}\\
|
||
|
||
\noindent Ha a tesztelés során hibajelenséggel találkozunk, akkor következhet a \emph{hibakeresés}, a hibajelenséget okozó utasítás megtalálása, majd pedig a \emph{hibajavítás}.\\
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent 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. Vizsgálnunk kell
|
||
\begin{itemize}
|
||
\item[-] a hatékonyságot (végrehajtási idő, helyfoglalás), másrészt
|
||
\item[-] a kényelmes felhasználhatóságot (felhasználó barát működés).
|
||
\end{itemize}
|
||
|
||
\noindent 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éhány – jónak tűnő – követelmény 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
|
||
\subitem (ez legegyszerűbben úgy érhető el, hogy ismert formalizmusokra építjük)
|
||
\item[-] szemléletes, érthető
|
||
\subitem (amit időnként nehezít a formalizáltság)
|
||
\end{itemize}
|
||
|
||
\noindent 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}
|
||
|
||
\noindent 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ítható, hogy a specifikációnak \emph{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}
|
||
|
||
\noindent Tudjuk-e, hogy a bemenő, illetve a kimenő változók milyen értéket vehetnek fel?
|
||
\begin{itemize}
|
||
\item[-] Például az emberek magasságát milyen mértékegységben kell megadni?
|
||
\item[-] Az eredményül kapott sorszám milyen érték lehet: 1-től sorszámozunk, vagy 0-tól?
|
||
\end{itemize}
|
||
|
||
\noindent Megállapíthatjuk tehát, hogy a specifikációnak \emph{tartalmaznia kell a bemeneti és a kimeneti változók értékhalmazá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 fejezik ki (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.
|
||
\end{verbatim}
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent A specifikációnak tehát \emph{tartalmaznia kell a feladatban használt fogalmak definícióját}, valamint az \emph{eredmény kiszámítási szabályát}.\\
|
||
|
||
\noindent Itt lehetne megadni a bemenő adatokra vonatkozó összefüggéseket is. A \emph{bemenő}, illetve a \emph{kimenő} adatokra kirótt feltételeket nevezzük \emph{előfeltétel}nek, illetve \emph{utófeltétel}nek. 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}
|
||
|
||
\noindent Ú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.\\
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent A probléma megoldására kétféle utat követhetünk:
|
||
\begin{itemize}
|
||
\item az utófeltételbe automatikusan beleértjük, hogy "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}
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent 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 \emph{feladat ún. nemdeterminisztikussága}. \\
|
||
|
||
\noindent 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.
|
||
\newpage
|
||
\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}
|
||
|
||
\noindent Megállapíthatjuk ebből, hogy \emph{a program utófeltétele lehet szigorúbb, mint a feladaté}, emellett \emph{az előfeltétele pedig lehet gyengébb}.\\
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent 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.\\
|
||
|
||
\noindent 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 Az elöbbi feladat progos specifikációja:\\\\
|
||
$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} \geq A_{i}\ \land\ \forall j \in \{1, ... ,MAX-1\}\ :\ A_{j} < A_{MAX})$
|
||
|
||
\subsection*{Tervezés}
|
||
|
||
A tervezés során algoritmusleíró eszközöket használunk, amelynek célja a feladatok megoldásának leírása programozási nyelvtől független nyelven. A programozási nyelvek ugyanis szigorú szintaxisúak, a tervezés szempontjából lényegtelen sallangokat tartalmaznak. A programozási nyelven történő tervezés esetén nehézzé válhat a program átírása más nyelvre, más gépre. Többféle algoritmusleíró eszköz is létezik, mi tanulmányaink során a struktogramot alkalmaztuk.\\
|
||
|
||
\noindent A \textbf{struktogram} a programgráfot élek nélkül ábrázolja. Így egyetlen egy alapelem marad, a \emph{téglalap}. Ezzel az alapelemmel építhetjük fel a szokásos strukturált alapszerkezeteket (és csak azokat).
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.7\linewidth]{img/stuki_alapszerk1}
|
||
\caption{A struktogram összetett alapszerkezetei.}
|
||
\label{fig:stuki_alapszerk1}
|
||
\end{figure}
|
||
|
||
\noindent \emph{Szekvenciánál} a téglalapok egymás alatti sorrendje dönti el a végrehajtás sorrendjét.\\
|
||
|
||
\noindent Az \emph{elágazásfeltétel} igaz értéke esetén az i betűvel jelölt bal oldali téglalap utasítását kell végrehajtani, hamis értéke esetén pedig az n betűvel jelölt jobb oldali téglalapét.\\ Ha az elágazás valamelyik ága üres, akkor a neki megfelelő téglalap is üres marad. \\
|
||
|
||
\noindent A \emph{ciklus} elöltesztelős, azaz a benne levő utasítást mindaddig végre kell hajtani, amíg a feltétel igaz.\\
|
||
|
||
\noindent Az utasítások helyén lehet egyetlen elemi utasítás, lehet a három algoritmikus szerkezet valamelyike, és lehet egy eljáráshívás.\\
|
||
|
||
\noindent Ezt a leíróeszközt még többféle elemmel szokták bővíteni:
|
||
\begin{itemize}
|
||
\item Eljárásdefinícióval
|
||
\item Sokirányú elágazással
|
||
\item Hátultesztelős ciklussal
|
||
\end{itemize}
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.7\linewidth]{img/stuki_alapszerk2}
|
||
\caption{A struktogram további összetett alapszerkezetei.}
|
||
\label{fig:stuki_alapszerk2}
|
||
\end{figure}
|
||
|
||
\noindent Sokirányú elágazásnál azt az ágat kell végrehajtani, amelynek igaz értékű a feltétele (közülük minden esetben pontosan egy teljesülhet).\\
|
||
|
||
\noindent A lokális adatokat az eljárások téglalapjai mellett, az eljárásnév után sorolhatjuk fel.\\
|
||
|
||
\noindent Nézzük meg ezzel az eszközzel leírva a következő példát!\\
|
||
|
||
\noindent Feladat: N tanuló év végi átlagának ismeretében adjuk meg a jeles átlagú tanulók számát!
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.3\linewidth]{img/stuki_pelda}
|
||
\caption{A példafeladat megoldása struktogrammal.}
|
||
\label{fig:stuki_pelda}
|
||
\end{figure}
|
||
|
||
\subsection*{Megvalósítás}
|
||
|
||
A program elkészítése a kiválasztott programozási nyelve(ke)n, azaz a kódolás.
|
||
\newpage
|
||
|
||
\subsection*{Tesztelés}
|
||
|
||
A tesztelés célja, hogy minél több hibát megtaláljunk a programban. Ahhoz, hogy az összes hibát fölfedezzük, kézenfekvőnek tűnik a programot kipróbálni az összes lehetséges bemenő adattal. Ez azonban általában sajnos nem lehetséges.\\
|
||
|
||
\noindent Példaként tekintsük a következő - pszeudokóddal megadott - egyszerű programot:
|
||
|
||
\begin{verbatim}
|
||
Program:
|
||
Változó A,B:Egész
|
||
Be: A,B
|
||
Ki: A/B
|
||
Program vége.
|
||
\end{verbatim}
|
||
|
||
\noindent Mivel $2^{16}$ különböző értékű egész számot tudunk tárolni, ezért az összes lehetőség $2^{32}$, aminek a leírásához már 9 számjegyre van szükség. Ez rengeteg időt venne igénybe, így nem is járható út.\\
|
||
|
||
\noindent Ha ezt a programot olyan bemenő adatokkal próbáljuk ki, amelyben A=0 vagy B=1, akkor a program helyesen működik, a hibát nem tudjuk felfedezni. Ezután azt gondolhatnánk, hogy reménytelen helyzetbe kerültünk: hiszen minden lehetséges adattal nem tudjuk kipróbálni a programot; ha pedig kevesebbel próbáljuk ki, akkor lehet, hogy nem vesszük észre a hibákat. A helyzet azért nem ennyire rossz: célunk csak az lehet, hogy a tesztelést olyan módszerrel hajtsuk végre, amellyel a próbák száma erősen lecsökkenthető.\\
|
||
|
||
\noindent Tesztesetnek a be- és kimeneti adatok és feltételek együttes megadását nevezzük. Akkor tudunk a tesztelés eredményeiről bármit is mondani, ha van elképzelésünk arról, hogy adott bemenő adatra milyen eredményt várunk.\\
|
||
|
||
\noindent Fogalmazzuk meg a tesztelés alapelveit:
|
||
\begin{itemize}
|
||
\item A jó teszteset az, ami nagy valószínűséggel egy még felfedetlen hibát mutat ki a programban.\\
|
||
Például két szám legnagyobb közös osztóját számoló programot az [5,5] adatpár után a [6,6]-tal teljesen felesleges kipróbálni (ugyanis igencsak rafinált, valószínűtlen elírás esetén viselkedhet a program [6,6]-ra másként, mint [5,5]-re).
|
||
\item A teszteset nemcsak bemenő adatokból, hanem a hozzájuk tartozó eredményekből is áll. Egyébként nem tudnánk a kapott eredmény helyes vagy hibás voltáról beszélni.\\
|
||
A későbbi felhasználás miatt célszerű a teszteseteket is leírni a fejlesztői dokumentációban vagy egy önálló tesztelési jegyzőkönyvben.
|
||
\item A meg nem ismételhető tesztesetek kerülendők, feleslegesen megnövelik a program-tesztelés költségeit, idejét. Nem is beszélve arról a bosszúságról, amikor a programunk egy hibás futását nem tudjuk megismételni, és így a hiba is felfedetlen marad.
|
||
\item Teszteseteket mind az érvénytelen, mind az érvényes adatokra kell készíteni.
|
||
\item Minden tesztesetből a lehető legtöbb információt "ki kell bányászni”, azaz minden teszteset eredményét alaposan végig kell vizsgálni. Ezzel jelentősen csökkenthető a szükséges próbák száma.
|
||
\item Egy próba eredményeinek vizsgálata során egyaránt fontos megállapítani, hogy miért nem valósít meg a program valamilyen funkciót, amit elvárunk tőle, illetve hogy miért végez olyan tevékenységeket is, amelyeket nem feltételeztünk róla.
|
||
\item A program tesztelését csak a program írójától különböző személy képes hatékonyan elvégezni. Ennek oka, hogy a tesztelés nem "jóindulatú” tevékenység, saját munkájának vizsgálatához mindenki úgy áll hozzá, hogy önkéntelenül jónak feltételezi.
|
||
\end{itemize}
|
||
|
||
\noindent A programtesztelés módszereit két csoportba oszthatjuk aszerint, hogy a tesztelés során végrehajtjuk-e a programot, vagy nem. Ha csak a program kódját vizsgáljuk, akkor \emph{statikus} (erről nem esik több szó), ha a programot végre is hajtjuk a tesztelés során, akkor \emph{dinamikus} tesztelésről beszélünk.
|
||
|
||
\paragraph{Dinamikus tesztelési módszerek}
|
||
|
||
A dinamikus tesztelési módszerek alapelve az, hogy a programot működés közben vizsgáljuk. Teszteseteket kétféle módon tudunk választani. \\
|
||
|
||
\noindent Egy lehetőség az ún. \textbf{feketedoboz-módszer}, más néven adatvezérelt tesztelés.\\
|
||
A módszer alkalmazásakor a tesztelő nem veszi figyelembe a program belső szerkezetét, pontosabban nem azt tekinti elsődleges szempontnak, hanem a teszteseteket a feladat meghatározás alapján választja meg.\\
|
||
|
||
\noindent A cél természetesen a lehető leghatékonyabb tesztelés elvégzése, azaz az összes hiba megtalálása a programban. Ez ugyan elvileg lehetséges, kimerítő bemenet tesztelést kell végrehajtani, a programot ki kell próbálni az összes lehetséges bemenő adatra. Ezzel a módszerrel azonban, mint korábban láttuk, mennyiségi akadályba ütközhetünk.\\
|
||
|
||
\noindent Egy másik lehetőség a \textbf{fehérdoboz-módszer} (logika vezérelt tesztelés). Ebben a módszerben a tesztesetek megválasztásánál lehetőség van a program belső szerkezetének figyelembevételére is.\\
|
||
|
||
\noindent A cél a program minél alaposabb tesztelése, erre jó módszer a kimerítő út tesztelés. Ez azt jelenti, hogy a programban az összes lehetséges utat végigjárjuk, azaz annyi tesztesetet hozunk létre, hogy ezt elérhessük vele. Az a probléma, hogy még viszonylag kis programok esetén is igen nagy lehet a tesztelési utak száma. Gondoljunk a ciklusokra! Sőt ezzel a módszerrel a hiányzó utakat nem lehet felderíteni.\\
|
||
|
||
\noindent Mivel sem a fehérdoboz-módszerrel, sem a feketedoboz-módszerrel nem lehetséges a kimerítő tesztelés, el kell fogadnunk, hogy nem tudjuk egyetlen program hibamentességét sem szavatolni. A további cél ezek után az összes lehetséges teszteset halmazából a lehető leghatékonyabb teszteset-csoport kiválasztása lehet.\\
|
||
|
||
\noindent A tesztelés hatékonyságát kétféle jellemző határozza meg: a tesztelés költsége és a felfedett hibák aránya. A leghatékonyabb teszteset-csoport tehát minimális költséggel maximális számú hibát fed fel.\\
|
||
|
||
\noindent A feketedoboz- és fehérdoboz-teszteken kívül még érdemes megemlíteni olyan speciális teszteket, amikor nem a helyesség belátása a cél. Ilyen pl. a stresszteszt (nagy adatmennyiséget hogyan bír kezelni a program, jól skálázódik-e) vagy a hatékonysági teszt (végrehajtási idő tesztelése).
|
||
|
||
\section*{Az adattípus fogalma}
|
||
|
||
\subsection*{Alapfogalmak, jelölések}
|
||
|
||
\begin{itemize}
|
||
|
||
\item Legyen\\
|
||
$\begin{array}{r|l}
|
||
A^{*} & A\text{-beli \emph{véges} sorozatok halmaza} \\
|
||
A^{\infty} & A\text{-beli \emph{végtelen} sorozatok halmaza} \\
|
||
A^{*} \cup A^{\infty} = A^{**} & A\text{-beli \emph{véges vagy végtelen} sorozatok halmaza} \\
|
||
\end{array}$
|
||
|
||
\item Legyen $R \subseteq A \times \mathbb{L}$ egy logikai reláció.\\
|
||
Ekkor az $R$ \emph{\textbf{igazsághalmaza}}
|
||
$\lceil R \rceil ::= R^{-1}(\left\{{igaz}\right\}) $
|
||
|
||
\item Legyen $I$ egy véges halmaz és legyenek $A_{i},\ i \in I$ tetszőleges véges vagy megszámolható, nem üres halmazok.\\
|
||
Ekkor az
|
||
\begin{itemize}
|
||
\item $A = \underset{i \in I}{\times} A_{i}$ halmazt \emph{\textbf{állapottér}}nek, az
|
||
\item $A_{i}$ halmazokat pedig \emph{\textbf{típus-érték halmaz}}oknak nevezzük.
|
||
\end{itemize}
|
||
\item Az $F \subseteq A \times A$ relációt \emph{\textbf{feladat}}nak nevezünk.\\
|
||
{\footnotesize A feladat fenti definíciója természetes módon adódik abból, hogy a feladatot egy leképezésnek tekintjük az állapottéren, és az állapottér minden pontjára megmondjuk, hova kell belőle eljutni, ha egyáltalán el kell jutni belőle valahova.}
|
||
|
||
\item Az $S \subseteq A \times A^{**}$ relációt \emph{\textbf{program}}nak nevezzük, ha
|
||
\begin{enumerate}
|
||
\item $\mathcal{D}_{S}=A$\\
|
||
{\footnotesize (az állapottér minden pontjához rendel valamit = a program minden pontban csinál valamit)}
|
||
\item $\forall \alpha \in \mathcal{R}_{S} : \alpha = red(\alpha)$\\
|
||
red: Egy $A^{**}$-beli sorozat redukáltja. Azonos elemek helyettesítése egy elemmel (distinct).
|
||
{\footnotesize(az állapot megváltozik, vagy ha mégsem, az az abnormális működés jele)}
|
||
\item $\forall a \in A : \forall \alpha \in S(A) : |\alpha| \not = 0$ és $\alpha_{1}=a$\\
|
||
\end{enumerate}
|
||
\end{itemize}
|
||
|
||
\noindent A fenti definícióval a "működés” fogalmát akarjuk absztrakt módon megfogalmazni.
|
||
|
||
\subsection*{Típusspecifikáció}
|
||
|
||
Először bevezetünk egy olyan fogalmat, amelyet arra használhatunk, hogy pontosan
|
||
leírjuk a követelményeinket egy típusértékhalmazzal és a rajta végezhető
|
||
műveletekkel szemben.\\
|
||
|
||
\noindent A $\mathcal{T_{S}}=(H,I_{S},\mathbb{F})$ hármast \emph{\textbf{típusspecifikáció}}nak nevezzük, ha
|
||
teljesülnek rá a következő feltételek:
|
||
|
||
\begin{enumerate}
|
||
\item H az alaphalmaz,
|
||
|
||
\item $I_{S} : H \to \mathbb{L} $ a \emph{specifikációs invariáns},
|
||
|
||
\item $T_{\mathcal{T}} = \left\{ {(\mathcal{T},x) | x \in \lceil I_{S}} \rceil\right\}$ a \emph{típusértékhalmaz},
|
||
|
||
\item $\mathbb{F} = \left\{ {F_{1},F_{2},...,F_{n}}\right\}$ a \emph{típusműveletek specifikációja}, ahol\\
|
||
$\forall i \in [1..n]: F_{i} \subseteq A_{i} \times A_{i}$, $A_{i} = A_{i_{1}} \times ... \times A_{i_{n_{i}}}$ úgy,\\
|
||
hogy $\exists j \in [1..n_{i}]: A_{i_{j}} = T_{\mathcal{T}} $
|
||
\end{enumerate}
|
||
|
||
\noindent Az alaphalmaz és az invariáns tulajdonság segítségével azt fogalmazzuk meg, hogy mi az a halmaz, $T_{\mathcal{T}}$, amelynek elemeivel foglalkozni akarunk, míg a feladatok halmazával azt írjuk le, hogy ezekkel az elemekkel milyen műveletek végezhetők el.\\
|
||
|
||
\noindent Az állapottér definíciójában szereplő típusértékhalmazok mind ilyen típusspecifikációban
|
||
vannak definiálva. Az állapottér egy komponensét egy program csak a típusműveleteken keresztül változtathatja meg.
|
||
|
||
\newpage
|
||
\subsection*{Típus}
|
||
|
||
\noindent Vizsgáljuk meg, hogy a típusspecifikációban leírt követelményeket hogyan valósítjuk meg.\\
|
||
|
||
\noindent A $\mathcal{T}=(\rho,I,\mathbb{S})$ hármast \emph{\textbf{típus}}nak nevezzük, ha
|
||
|
||
\begin{enumerate}
|
||
\item $\rho \subseteq E^{*} \times T$ a reprezentációs függvény (reláció),\\
|
||
T a típusértékhalmaz,\\
|
||
E az elemi típusértékhalmaz
|
||
\item $I : E^{*} \to \mathbb{L} $ típusinvariáns
|
||
\item $\mathbb{S} = \left\{ {S_{1},S_{2},...,S_{m}}\right\}$, ahol\\
|
||
$\forall i \in [1..m]: S_{i} \subseteq B_{i} \times B_{i}^{**}$ program, $B_{i} = B_{i_{1}} \times ... \times B_{i_{m_{i}}}$ úgy,\\
|
||
hogy $\exists j \in [1..m_{i}]: B_{i_{j}} = E^{*}$ és $\not \exists j \in [1..m_{i}]: B_{i_{j}} = T$
|
||
\end{enumerate}
|
||
|
||
\noindent A típus első két komponense az absztrakt típusértékek reprezentációját írja le, míg a programhalmaz a típusműveletek implementációját tartalmazza. Az elemi típusértékhalmaz lehet egy tetszőleges másik típus típusértékhalmaza vagy egy, valamilyen módon definiált legfeljebb megszámolható halmaz.
|
||
|
||
\subsection*{Invariáns}
|
||
|
||
Az invariáns lényege, hogy ezt a tulajdonságot soha nem sérthetjük meg. Például halmaz típus esetén nem szabad, hogy megsérüljön
|
||
az az invariáns tulajdonság, hogy egy halmazban egy elem csak egyszer fordulhat elő.
|
||
|
||
\subsection*{Reprezentáció}
|
||
|
||
Azt, hogy egy típust milyen típusok segítségével, milyen módszerrel, stb., valósítottunk meg, reprezentációnak nevezzük. Például
|
||
egy verem típust meg lehet valósítani tömb segítségével, de láncolt listával is.\\
|
||
|
||
\noindent A reprezentáció a típusspecifikáció típusértékhalmazának leképezése a konkrét típusban, amit a reprezentációs függvény ad meg.
|
||
|
||
\subsection*{Implementáció}
|
||
|
||
Az implementáció a típusspecifikáció típusműveleteinek megvalósítása a konkrét típus programhalmaza által.\\
|
||
|
||
\noindent Az implementáció során a típus megvalósításakor a típusértékhalmaz megadását követően definiálni kell a típusműveleteket. Ahogyan a modellben is, a gyakorlatban is az állapottér változásait a program csak a típusműveleteken keresztül végezheti el.
|
||
|
||
\subsection*{Emészthetőbb módon}
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.75\linewidth]{img/adattipus}
|
||
\caption{Adattípus}
|
||
\label{fig:adattipus}
|
||
\end{figure}
|
||
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.75\linewidth]{img/adattipus_pelda}
|
||
\caption{BigNumber példa}
|
||
\label{fig:adattipus_pelda}
|
||
\end{figure}
|
||
\newpage
|
||
|
||
\section*{A visszavezetés módszere}
|
||
|
||
A programozási feladatok megoldásához különböző programozási mintákat, ún. programozási tételeket használunk fel, ezekre vezetjük vissza
|
||
a megoldást.\\
|
||
|
||
\noindent A visszavezetés lépései:
|
||
\begin{enumerate}
|
||
\item Megsejtjük a feladatot megoldó programozási tételt.
|
||
\item Specifikáljuk a feladatot a programozási tétel jelöléseivel.
|
||
\item Megadjuk a programozási tétel és a feladat közötti eltéréseket:
|
||
\begin{itemize}
|
||
\item intervallum határok: konkrét érték vagy kifejezés (pl. $[1..\frac{n}{2}]$),
|
||
a típusuk a $\mathbb{Z}$ helyett lehet annak valamely része (pl. $\mathbb{N}$)
|
||
|
||
\item $\beta:[m..n] \to \mathbb{L}$ és/vagy $f:[m..n] \to H$ konkrét megfelelői
|
||
|
||
\item a H megfelelője a szükséges művelettel
|
||
\begin{itemize}
|
||
\item $(H,>)$ 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.
|
||
{\color{mygray}
|
||
\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}
|
||
|
||
\noindent Egy felsorolásnak különböző állapotai vannak
|
||
\begin{itemize}
|
||
\item indulásra kész
|
||
\item folyamatban van
|
||
\item befejeződött\\
|
||
\end{itemize}
|
||
|
||
\noindent A műveletek csak bizonyos állapotokban értelmezhetők (máshol a hatásuk nem definiált). \\
|
||
|
||
\noindent 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.3\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}
|
||
|
||
A következő szakaszban találhatóak a felsorolókra megfogalmazott programozási tételek progos definíciója, valamint stuktorgramja.
|
||
|
||
\includepdf[pages={1-2}]{progtetel_felsorolo.pdf}
|
||
|
||
\section*{Nevezetes gyűjtemények felsorolói}
|
||
|
||
\subsection*{Intervallum}
|
||
\begin{figure}[H]
|
||
\centering
|
||
\includegraphics[width=0.75\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}
|
||
|
||
\noindent \emph{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}
|
||
|
||
\end{document} |