\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}<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.

A struktogram a programgráfot élek nélkül ábrázolja. Így egyetlen egy alapelem marad, a 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}

Szekvenciánál a téglalapok egymás alatti sorrendje dönti el a végrehajtás sorrendjét. Az 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. A ciklus elöltesztelős, azaz a benne levő utasítást mindaddig végre kell hajtani, amíg a feltétel igaz.

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. Ezt a leíróeszközt még többféle elemmel szokták bővíteni: az eljárásdefinícióval, a sokirányú elágazással, illetve a hátultesztelős ciklussal.

\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.4\linewidth]{img/stuki_pelda}
    \caption{A példafeladat megoldása struktogrammal.}
    \label{fig:stuki_pelda}
\end{figure}

\subsection{Megvalósítás}

A.k.a. kódolás.
\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 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}

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.

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ő.\\

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}

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 statikus (erről nem esik több szó), ha a programot végre is hajtjuk a tesztelés során, akkor 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. Egy lehetőség az ún. feketedoboz-módszer, más néven adatvezérelt tesztelés. E 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.

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.

Egy másik lehetőség a 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.

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.

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.

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.

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	$A^{*}$ az A-beli véges sorozatok halmazát, $A^{\infty}$ az A-beli végtelen sorozatok halmazát jelöli.
          A kettő uniója $A^{**} = A^{*} \cup A^{\infty}$ pedig az A-beli véges vagy végtelen sorozatok halmazát jelenti.

    \item	Legyen $R \subseteq A \times \mathbb{L}$ egy logikai reláció. Ekkor az R 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őlege véges vagy megszámolható, nem üres halmazok.
          Ekkor az $A = \underset{i \in I}{\times} A_{i}$ halmazt állapottérnek, az $A_{i}$ halmazokat pedig típusértékhalmazoknak nevezzük.

    \item	Feladat: feladatnak nevezünk egy $F \subseteq A \times A$ relációt.\\
          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 Program:\\
          Programnak nevezzük az $S \subseteq A \times A^{**}$ relációt, ha
          \begin{enumerate}
              \item	$\mathcal{D}_{S}=A$ (az állapottér minden pontjához rendel valamit, azaz a program minden pontban csinál valamit)
              \item	$\forall \alpha \in \mathcal{R}_{S} : \alpha = red(\alpha)$ (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}
          \noindent A fenti definícióval a "működés” fogalmát akarjuk absztrakt módon megfogalmazni.

\end{itemize}

\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 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 specifikációs invariáns,

    \item $T_{\mathcal{T}} = \left\{ {(\mathcal{T},x) | x \in \lceil I_{S}} \rceil\right\}$ a típusértékhalmaz,

    \item $\mathbb{F} = \left\{ {F_{1},F_{2},...,F_{n}}\right\}$ a 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}

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.

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.
\subsection{Típus}

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 típusnak 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}

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.

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.

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.7\linewidth]{img/adattipus}
    \caption{Adattípus}
    \label{fig:adattipus}
\end{figure}

\begin{figure}[H]
    \centering
    \includegraphics[width=0.7\linewidth]{img/adattipus_pelda}
    \caption{BigNumber példa}
    \label{fig:adattipus_pelda}
\end{figure}

\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 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.
\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}

\end{document}