Személyes eszközök
Keresés

 

A InfoWiki wikiből

(Változatok közti eltérés)
Aktuális változat (2009. november 14., 22:08) (lapforrás)
 
(3 közbeeső változat nincs mutatva)
1. sor: 1. sor:
<cim  
<cim  
-
cim3="Előírt lépésszámú ciklusok"
+
cim3="Vektorok"
cim2="Magasszintű Programozási Nyelvek I."  
cim2="Magasszintű Programozási Nyelvek I."  
cim1="Imperatív, procedurális nyelvek alapjai"  
cim1="Imperatív, procedurális nyelvek alapjai"  
15. sor: 15. sor:
-
= Előírt lépésszámú ciklusok =
+
= Vektorok =
-
Tekintsük meg az alábbi kódot:
+
Gyakori probléma, hogy annyi adattal kell dolgoznunk, hogy kényelmetlen és zavaró lenne minden egyes
 +
adatnak saját változót deklarálni. Kényelmesebb lenne egyetlen változót használnunk, amely egy időben
 +
több adat tárolását is képes lenne megoldani. Az elv hasonló, mint a szemétkosár. Kényelmesebb
 +
összegyűjteni a szemétdarabkáinkat egy konténerbe, mint egyesével kezelni őket.
 +
 
 +
Ennek feltétele, hogy a sok (több) adat egyforma típusú legyen. A második félévben (OOP) látni
 +
fogjuk, hogy ennek gyakorlatilag semmi akadálya nincs, ha adataink alapvetően különböző típusúnak
 +
látszanak is, akkor is mindíg megoldható, hogy egyforma típusúnak látszódjanak (típuskompatibilitás).
 +
 
 +
Ebben a félévben azonban csak azzal az esettel foglalkozunk, amikor az adataink tényleg egyforma típusúak.
 +
Például egy adott időszak napi átlaghőmérséklet adatai. Ha az időszak két hónap, akkor körülbelül 60 darab
 +
double adattal kell egy időben dolgoznunk.
<code lang="csharp">
<code lang="csharp">
-
int i=0;
+
double a = 1.2;
-
while (i<10)
+
-
{
+
-
  ...
+
-
  i++;
+
-
}
+
</code>
</code>
-
A fenti ''while'' ciklus a ciklusmagját 10-szer fogja végrehajtani, mivel az ''i'' változó induló értéke 0,
+
A fenti kódban létrehoztunk egyetlen double tárolására képes változót. Az alábbi kódban létrehozunk
-
minden menetben az ''i'' értéke pontosan 1-el nő, és akkor fogja elhagyni a ciklust, ha az ''i'' értéke elérte
+
egy 60 double tárolására képes változót:
-
(vagy meghaladta) a 10 értéket.
+
-
Gyakoriak azok az esetek, amikor előre ismert, hogy a ciklusmag hányszor kerül majd végrehajtásra. Ez esetben
+
<code lang="csharp">
-
a fentihez hasonló while ciklus alkalmazható, de nem túl szerencsés az alkalmazása. Az információk, amelyből
+
double[] t;
-
a kiderül, hogy ez a ciklus 10-szer ismétlődik majd - a kódban szétszórtan helyezkedik el. Ráadásul az ''i''
+
</code>
-
változó deklarációja meg kell előzze a while ciklus indulását, így hatásköre (és az élettartama) nem a ciklusra
+
-
korlátozódik,  hanem a ciklus lefutása után is megmarad.
+
-
A fenti problémák kezelésére speciális ciklusfajta szolgál, a '''for''' ciklus.
+
A fenti ''t'' változó típusában (''double[]'') látszik, hogy itt nem egyetlen double, hanem több double
 +
adat összefogását kell elvégeznünk. A tényleges darabszám egyelőre nem látszik.
-
<box type="warn">A FOR ciklus akkor alkalmazható, ha a ciklus futása előre ismert darabszámú, mely darabszám a ciklus indításakor már kiszámolható, és később sem változik. Ha a ciklus futási darabszáma nem előre kiszámítható, vagy esetleg az változhat, akkor inkább WHILE ciklust kell alklamazni.</box>
+
= Összetett adattípusok =
 +
A fenti példában szereplő ''t'' változó alapvetően különbözik a korábban használt, egyszerű ''double'' változóktól.
 +
Az alaptípusának neve mögött szereplő két szögletes zárójel a tömb képző típusmódosító operátor jele. Jelenéte
 +
arra hívja fel a figyelmet, hogy nem egyetlen double, hanem sok double fog a ''t'' változóba kerülni.
-
= FOR fejrésze =
+
Az ilyen típusú változókat, amelyek egy időben több érték tárolására is képesek - '''összetett típusú''' változóknak
 +
nevezzük. Ez a tulajdonságuk alapvetően megkülönbözteti őket az '''egyszerű típusoktól''', amelybe eső változók
 +
egy időben csak egyetlen értéket tartalmazhatnak.
-
A ''for'' ciklus fejrésze három részre osztható, melyeket kettő darab pontosvessző határol. A részek szerepe:
+
= Műveletek =
-
* az inicializáló utasítás (mely a ciklus kezdetekor egyszer hajtódik végre
+
-
* a ciklus vezérlő feltétele (pozitív vezérlésű a for ciklus)
+
-
* a léptető utasítás (mely a ciklusmag lefutása után automatikusan kerül végrehajtásra)
+
-
Példa:
+
Nem kell persze nagy dolgokra számítani. Az összetett típusú változókkal jellemzően nem lehet műveletet végezni,
 +
valójában egy időben mindíg csak egyik elemükkel dolgozunk (egy érték). Ezen egy elemük már elemi típusú értékként
 +
viselkedik, tehát rá minden művelet alkalmazható, amit az adott egyszerű típusra megismertünk.
-
<code lang="csharp">
+
= Jellemzés =
-
for(int i=0; i<10; i++)
+
-
{
+
-
  Console.Write(i);
+
-
}
+
-
</code>
+
-
A fenti példa teljesen megfelel a korábban ismertetet while ciklus példában szereplő kód működésével. Az
+
Összetett adattípusokból is több van, őket különböző jellemzőik különböztetik meg egymástól:
-
''i'' változó azonban itt a ciklusra lokális, tehát a for ciklus törzsén túl már nem terjed a hatásköre.
+
-
A ciklusmag minden lefutása után végrehajtódik az inkrementáló utasítás, vagyis az ''i'' értéke nő 1-el.
+
-
A példában szereplő for ciklus fejrészében minden információ szerepel, melyből meghatározható, hogy
+
* '''tárolási kapacitás''': ebből alapvetően két fajta van:
-
ez a for ciklus pontosan 10-szer fog lefutni. A ciklusmagban az ''i'' változó értéke kiolvasható,  
+
** ''statikus kapacitás'': az ilyen típusok képesek ugyan sok adat tárolására, de ezen adatok számát jó előre meg kell határozni. Az adott változó végig ugyanennyi adatot tárol, sem többet sem kevesebbet. Az adatok darabszámát a változó létrehozásakor rögzíteni kell.
-
de megváltoztatása tilos. Ennek oka, hogy az ''i'' változó értékének átírása a pontos 10 ismétlést
+
** ''dinamikus kapacitás'': az ilyen változókban tárolt adatok száma időben változhat. Új adatot tehetünk be, korábban betett adatot később törölhetünk. A változó létrehozásakor esetleg megadható egy kezdeti darabszám, de ettől később bármikor eltérhetünk könnyedén. Előfordul az az eset is, amikor a változó létrehozásakor még nincs is benne egy adat sem (nulla elemszám).
-
módosítaná (vagy kevesebbszer, vagy többször kerülne végrehajtásra).  
+
-
<box type="warn">A ciklusváltozó ciklusmagon belüli módosítása nem szintaktikai, hanem elvi hiba. A fordító eltűri, de ezen tevékenység ellentmond a for ciklus lényegének. Akinek ilyenre van szüksége, az használjon while ciklust.</box>
+
* '''memóriabeli elhelyezkedés''': ez a jellemző azt mutatja, hogy a sok adat a memóriában hogyan helyezkedik el. Ezt a jellemzőt ''tárolási reprezentációnak'' is szokták nevezni:
 +
** ''folytonos'': a tárolt adatok a memória egy összefüggő területén helyezkendnek el, általában szorosan egymás mellett, mögött.  
 +
** ''szétszórt'': a folytonos ellentéte, az egyes elemek egymástól akár nagyobb távolságra is elhelyezkedhetnek, nem jellemzi ezt a távolságot semmi, oda kerülnek, ahol szabad hely volt a memóriában abban a pillanatban.
-
= Példa =
+
* '''típusosság''': nem feltétlenül igaz, hogy a sok adat mindíg egyforma típusú, bár az adott összetett típus ezt kötelezővé teheti:
 +
** ''homogén'': az ilyen összetett adattípus megköveteli, hogy az összes adat egyforma típusú legyen
 +
** ''inhomogén'': ez esetben nem követelmény, hogy minden adat egyforma típusú legyen
-
Hibás tehát az alábbi példa (bár nem szintaktikailag, hanem elvi, stílusbeli hiba). A programban
+
 
-
a 10 elemű tömböt csak pozitív számokkal kívánjuk feltölteni. Amennyiben a program kezelője valamely
+
* '''adathozzáférési stratégia''': ez egy bonyolult jellemző. azt mutatja meg, hogy amennyiben szükségünk van valamely, az összetett típusú változónkban tárolt ''n'' sorszámú adatra, akkor milyen lépéssorozat kell az érték előkereséséhez
-
elem értékének negatív számot kíván megadni, azt az elemet újra bekérjük:
+
** ''direkt'' hozzáférés: a legjobb, ez esetben az adott adat sorszámának ismeretében az adat azonnal elérhető. Az adatelérés sebessége (ideje) nem az ''n'' értékétől függ (egyenletes sebesség)
 +
** ''szekvenciális'': az ''n'' sorszámú adat eléréséhez minden előtte lévő elemet el kell érni (a feldolgozás az első adattal kezdődik, és halad sorban amíg el nem érjük az ''n'' sorszámút). Ekkor nyilvánvaló, hogy ha ''n'' kicsi, akkor gyorsabban hozzá tudunk férni az értékhez, mint nagyobb ''n'' esetekben
 +
** ''FIFO'': speciálisan a ''sor'' (az ékezetek hiánya nem hiba) adatszerkezet jellemzője. Az adatok csakis abban a sorrendben kerülhetnek feldolgozásra, amilyen sorrendben az adatszerkezetbe bekerültek (first in first out)
 +
** ''LIFO'': speciális a ''verem'' adatszerkezet jellemzője. Az adatok csakis fordított sorrendben dolgozhatók fel, mint amilyen sorrendben az adatszerkezetbe bekerültek (last in first out)
 +
 
 +
Vannak további, nagyon speciális adatelérések, mint az indexelt szekvenciális, a hash, a logaritmikus, stb. A fentiekről
 +
azért nem írunk részletesen, mert ez inkább az ''Algoritmusok és Adatszerkezetek'' tárgy témája.
 +
 
 +
 
 +
= Példák =
 +
 
 +
A részletes tárgyalást mellőzve adunk egy listát, amely az egyes (az informatikában előforduló) összetett adatszerkezeteket
 +
jellemzi:
 +
 
 +
* ''tömb'': statikus, homogén, folytonos, direkt
 +
* ''lista'': dinamikus, homogén, folytonos, direkt
 +
* ''láncolt lista'': dinamikus, homogén, szétszórt, szekvenciális
 +
* ''verem'': dinamikus, homogén, folytonos, fifo
 +
* ''sor'': dinamikus, homogén, folytonos, lifo
 +
* ''rekord'': statikus, inhomogén, folytonos, direkt
 +
* ''fa'': dinamikus, homogén, szétszórt, (egyfajta) szekvenciális
 +
* ''szótár'': dinamikus, homogén, folytonos v. szétszórt, hash
 +
 
 +
= Vektorok =
 +
 
 +
A vektorok speciális (egydimenziós) tömbök (igazából a tömb a fenti jellemzőkkel bíró típusok összefoglaló neve).  
 +
Ezért a vektorok statikus, homogén, folytonos, direkt elérésű összetett adatszerkezetek.
 +
 
 +
Mivel homogének, létrehozásukkor meg kell adni az alaptípust. Ez szerepel a ''double[]'' típusnévben. A ''[]'' operátor
 +
jelzi, hogy nem egy double-ről beszélünk, hanem több double-ről.
 +
 
 +
A vektor statikus, vagyis induláskor meg kell adni a méretét. Ez nem szerepel a ''double[]'' típusnévben. Ez ugyanis
 +
absztrakt típusnév, az összes double alaptípusból álló vektor közös típusneve. Konkrét double vektor esetén konkrét
 +
méretet kell adni. Ezt a ''t'' változó kezdőértékének beállításakor kell közölni:
<code lang="csharp">
<code lang="csharp">
-
int[] tomb = new tomb[10];
+
double[] t = new double[30];
-
for(int i=0;i<10;i++)
+
-
{
+
-
  Console.Write("{0}. szám értéke:");
+
-
  tomb[i] = int.Parse(Console.ReadLine());
+
-
  if (tomb[i]<0) i--;
+
-
}
+
</code>
</code>
-
A feltételben az ''i--'' utasítás az ''i'' értékét csökkenti 1-el, amit
+
Ebben az esetben a ''new'' operátor a memóriafoglalást jelöli (a konkrét méretű double vektornak konkrét mennyiségű
-
az inkrementáló utasítás (''i++'') meg fog növelni 1-el. E miatt negatív
+
memóriaterületet kell lefoglalni. Ebben a példában ez 30*8=240 byte. Ezt a new operátor az alaptípus helyigényéből (egy
-
szám beírása esetén az ''i'' értéke gyakorlatilag nem fog változni - a ciklus
+
double 8 byte), és a méretből (30 darab double) önállóan számolja ki (valójában ezt az értéket a fordítóprogram fordításkor
-
következő lefutásakor is ugyanazon ''i'' tömbelem kerül feltöltésre.
+
számolja ki, a generált kódban a new mögött már a konkrét 240 érték szerepel, a new-t ugyanis csak a méret érdekli, a  
 +
felhasználási cél és mód már nem).
-
Ez elegánsabban megoldható while ciklussal:
+
Amennyiben a fenti kezdőértékadást nem adjuk ki, a ''t'' változó nem képes egyetlen double adat tárolására sem. Az ilyen
 +
próbálkozásokat a fordítóprogram még fordításkor kiszúrja, és szintaktikai hibával leáll. Bár a vektor típusú változó
 +
deklarációja, és a helyfoglalás elválhat egymástól, de ritkán történik ilyen. Jellemzően a vektor deklarálásakor annak
 +
helyfoglalása is megtörténik.
<code lang="csharp">
<code lang="csharp">
-
int[] tomb = new tomb[10];
+
double[] t;
-
int i=0;
+
...
-
while (i<10)
+
t = new double[30];
-
{
+
-
  Console.Write("{0}. szám értéke:");
+
-
  tomb[i] = int.Parse(Console.ReadLine());
+
-
  if (tomb[i]>0) i++;
+
-
}
+
</code>
</code>
-
Vagyis csak akkor lépünk a következő tömbelem bekérésére, ha ezen tömbelemmel
 
-
elégedettek vagyunk.
 
-
Másik lehetséges megoldás:
+
= Vektor feltöltése billentyűzetről =
 +
Egy (a példában szereplő) 30 elemű double vektorba 30 különböző double érték tárolható. A vektorok elemei induláskor
 +
típusuknak megfelelő nulla kezdőértékkel rendelkeznek. A szám (int, double,...) típusú vektorok ezért induláskor már
 +
0-kal vannak feltöltve. Ettől eltérő értéket elhelyezhetünk a vektor valamely elemében egy értékadó utasítással.
 +
Ekkor meg kell mondani, melyik elembe kívánunk értéket írni. Az elemeket sorszámukkal azonosítjuk, a sorszámok
 +
0-tól indulnak.
 +
 +
<box type="warn">Egy 30 elemű double vektor elemeinek sorszáma 0,1,2,..,28,29. A sorszámok mindíg 0-tól indulnak, még akkor is, ha ez nekünk nem tetszik (idővel meg lehet szokni). Más programozási nyelveknél a szabály eltérő is lehet: a BASIC-ben pl a sorszámozás 1-től indul. Pascal-ban a sorszámozás (szinte) tetszőlegesen választható.</box>
<code lang="csharp">
<code lang="csharp">
-
int[] tomb = new tomb[10];
+
double[] t = new double[30];
-
for(int i=0;i<10;i++)
+
int i = 0;
 +
while (i<30)
{
{
-
   do
+
   Console.Write("Kérek egy számot");
-
  {
+
  double x = double.Parse( Console.ReadLine() );
-
    Console.Write("{0}. szám értéke:");
+
   t[i] = x;
-
    tomb[i] = int.Parse(Console.ReadLine());
+
   i++;
-
   }
+
-
   while (tomb[i]<0);
+
}
}
</code>
</code>
-
Ez esteben az ''i''-edik tömbelem bekérésől addig nem lépünk tovább, amíg
+
A vektor valamely elemére hivatkozás során meg kell adni a vektor nevét (''t''), és a sorszámot (''i''), a sorszámot
-
annak értékével elégedettek nem vagyunk.
+
a vektor neve mögé írt szögletes zárójelpárba kell elhelyezni. A ''t[i]'' olvasata: ''a t vektor i. eleme'', vagy
 +
''a t vektor i sorszámú eleme''.
-
= Trükkösebb ciklusok (a) =
+
A fenti esetben a tömbbe a billentyűzetről beírt számokat helyezzük el. Figyeljük meg, hogy a sorszámok tárolására,
 +
kezelésére az ''i'' változót használjuk. A sorszámok ''mindíg egész'' számok, egész a sorszámozást végző változónk
 +
mindíg int (vagy valamelyik testvére). A vektor első elemének sorszáma 0, ezért az ''i'' változónkba ezt az értéket
 +
helyezzük el induláskor. Az első bekért szám a ''t[0]'' pozícióra kerül. Az ''i++'' miatt a sorszámunk lép egyet,
 +
és a következő bekért szám már a ''t[1]'' pozícióra kerül. Az ''i<30'' miatt az utolsó sorszám, amire végrehajtódik
 +
a szám elhelyezés a vektorban az a ''t[29]'' pozíció. Ugyan az ''i'' ekkor is nő egyet, de ''i=30''-ra már
 +
nem kerül végrehajtásra a ciklus.
-
A for ciklus fejrészében szereplő vezérlő feltétel tetszőlegesen összetett lehet,
+
<box type="warn">Egy ilyen ''t'' vektornak 0..29 sorszámú elemei vannak. Egyéb sorszámú elemekre (kisebb v. nagyobb sorszámok használata) való hivatkozás futás közbeni hibát generál, és a program azon a ponton leáll. Mindíg nagyon gondoljuk át, nehogy ezt elvétsük!</box>
-
hiszen az gyakorlatilag megfelel a while ciklus vezérlő feltételének szerepkörével:
+
 
 +
''Megjegyzés'': a fenti kódban az ''x'' változóra igazából nincs szükség, a bekért értéket közvetlenül a vektorba
 +
is elhelyezhetjük:
<code lang="csharp">
<code lang="csharp">
-
int db=0;
+
double[] t = new double[30];
-
for(int i=0;i<10 && db<3; i++)
+
int i = 0;
-
   if (tomb[i]>0) db++;
+
while (i<30)
 +
{
 +
   Console.Write("Kérek egy számot");
 +
  t[i] = double.Parse( Console.ReadLine() );
 +
  i++;
 +
}
</code>
</code>
-
Ez a for ciklus legkésőbb 10 menetben leáll. Korábban is leállhat, ha
+
= Vektor feltöltése véletlen számokkal =
-
a tömbben találtunk 3 pozitív számot.
+
-
Ez a működés némiképp ellentmond a korábban említett elvvel: a for ciklus
+
A fenti megoldáshoz nagyon hasonló, amikor a vektort nem billentyűzetről, hanem véletlen számokkal töltjük fel:
-
működésének darabszáma előre kiszámított kell legyen. Nos, a ez a határeset
+
-
kategória. Az ''i'' változó szerepe a tömb elemeinek indexelése, és ez a ciklus
+
-
sorra veszi a tömbelemeket. A ciklus ''maximális'' végrehajtási száma a fejben
+
-
adott (ez 10 darab). A korábbi kilépő feltétel belefér.
+
-
= Trükkösebb ciklusok (b) =
 
-
 
-
Az inkrementáló lépés nem mindíg az 1-el növelés. Tetszőleges utasítás megadható
 
-
azon a ponton:
 
<code lang="csharp">
<code lang="csharp">
-
for(double a=0;a<1.5; a=a+0.1) { ... }
+
double[] t = new double[30];
-
for(int i=10;i>0; i-- ) { ... }
+
Random rnd = new Random();
-
for(int i=2;i<100; i=i+2 ) { ... }
+
int i = 0;
-
for(int i=2;i<100; i=i*2-1 ) { ... }
+
while (i<30)
 +
{
 +
  t[i] = rnd.Next(1,100);
 +
  i++;
 +
}
</code>
</code>
-
= Trükkösebb ciklusok (c) =
+
= Vektor kiírása a képernyőre =
-
A C nyelvben szerepel egy misztikus jellemzőjű vessző operátor (, operátor). A
+
Gyakran van arra szükség, hogy a vektorban lévő elemeket a képernyőre is kiírjuk. Ez szolgálhat ellenőrzési szerepet
-
vessző operátorral lehetséges egyetlen utasítást megengedő helyre több utasítást
+
(látni akarjuk, hogy a vektorban tényleg benne vannak a számok, és melyek azok). Amikor a vektort véletlen számokkal
-
is szerepeltetni. Erre szükség lehet olyan esetekben, amikor adott pontján
+
töltjuk fel, természetes igény azokat meg is tekinteni.
-
a programnak tilos programblokkot alkalmazni, mégis szeretnénk több utasítást
+
-
is végrehajtani. Ilyen hely a for ciklus léptető (inkrementáló) utasítsása.
+
-
Helytelen az alábbi:
+
<code lang="csharp">
<code lang="csharp">
-
int ossz=0;
+
...
-
for( int i=0; i<10; {ossz=ossz+t[i];i++}) {}
+
int i = 0;
-
   Console.WriteLine("Az összeg={0}",ossz);
+
while (i<30)
 +
{
 +
   Console.WriteLine("{0}. sorszámú elem={1}",i,t[i]);
 +
  i++;
 +
}
</code>
</code>
-
+
 
-
Ezzel szemben helyes az alábbi:
+
Amikor sok elemről van szó, egy takarékosabb megoldást szoktunk választani: az elemeket a képernyőre
 +
egymás mellé, pl. vesszővel elválasztva írjuk ki:
<code lang="csharp">
<code lang="csharp">
-
int ossz=0;
+
...
-
for( int i=0; i<10; ossz=ossz+t[i],i++) {}
+
int i = 0;
-
Console.WriteLine("Az összeg={0}",ossz);
+
while (i<30)
 +
{
 +
  Console.Write("{0}, ",t[i]);
 +
  i++;
 +
}
 +
Console.WriteLine();
</code>
</code>
-
A vessző operátor súlyos megkötése, hogy a vesszővel elválasztott utasítások adott
+
A bezáró üres ''Console.WriteLine()'' szerepe, hogy az utolsóként kiírt vektorelem mögött álló kurzort
-
sorrendben hajtódnak végre (szekvencia szabály speciális esetre alkalmazása). Ezért
+
a következő sor elejére rakja át, így megakadályozva, hogy a program következő kiírásai ugyanide kerüljenek.
-
a fenti for ciklus még jól is működik.
+
-
Vegyük észre, hogy ebben az esetben a for ciklus magja üres. Ez megfelel annak
+
= Vektor mérete =
-
az esetnek, amikor a for ciklus magja egyetlen üres utasítást tartalmaz. Ez
+
-
esetben a pontosvessző írása a for ciklus fejrésze után nem hiba, hanem ezen
+
-
üres utasítás lezárása:
+
-
<code lang="csharp">
+
A vektor mérete létrehozásakor adott. Ugyanakkor a vele dolgozó ciklusokban vezérlő feltételében is folyton
-
int ossz=0;
+
előkerül. Amennyiben változik a vektor mérete, a ciklusok vezérlő feltételét is át kell(ene) írni. Erre
-
for( int i=0; i<10; ossz=ossz+t[i],i++) ;
+
a problémára két megoldás szokott születni.
-
Console.WriteLine("Az összeg={0}",ossz);
+
-
</code>
+
-
A vessző operátor azonban a C nyelv azon misztikus, és sokat vitatott megoldása,
+
Az első szerint a vektor méretét egy konstanban (vagy változóban) tároljuk el, és a ciklusokban is erre
-
amely (mint kiderült) több baj forrása, mint amennnyi problémát megold. Ezért
+
az értékre hivatkozunk:
-
a fenti kód a C# nyelvben bár működik, de kerülendő.
+
-
+
-
= A for fejrésze =
+
-
A for ciklus fejrésze minden esetben három részből áll, akkor is, ha valamely
 
-
utasítás az '''üres''' utasítás. A for ciklus fejrészéből csak a vezérlő
 
-
feltétel nem lehet üres, de mind a kezdőértékbeállítás, mind a léptetés
 
-
üresen hagyható:
 
<code lang="csharp">
<code lang="csharp">
-
int i=int.Parse(Console.ReadLine());
+
const int N = 20;
-
for(  ;i<10; i++)  
+
bool[] l = new bool[N];
-
{
+
int i = 0;
-
    ...  
+
while (i<N)
-
}
+
{
 +
  t[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
 
-
A for ciklus fejrésze ekkor is három részből kell álljon! Vagyis az üres
 
-
utasítást is le kell zárni a pontosvesszővel.
 
<code lang="csharp">
<code lang="csharp">
-
int i=0;
+
Console.WriteLine("hány adatról van szó:");
-
for( ;i<10; )  
+
int N = int.Parse( Console.ReadLine() );
-
{
+
bool[] l = new bool[N];
-
    ...  
+
int i = 0;
-
    i++;
+
while (i<N)
-
}
+
{
 +
  l[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
Ebben a példában szereplő for ciklus fejrészéből már mindkét elhagyható
+
Ez a megoldás szinte minden programozási nyelvben változatlan formában létezik. Néhány programozási nyelv (mint
-
utasítás elhagyásra is került. Ez már gyakorlatilag megfelel a korábban
+
pl a PHP is) függvényt biztosít a vektor méretének utólagos lekérdezésére (ekkor ez a szám használható a
-
felírt while ciklus működésének.
+
vezérlő feltételben):
 +
<code lang="php">
 +
$l = array(2,3,5);
 +
...
-
<code lang="csharp">
+
int i = 0;
-
for(int i=0;i<10; )  
+
while (i<sizeof($t))
-
{
+
{
-
    ...  
+
  l[i] = ...;
-
    i++;
+
  i++;
-
}
+
}
</code>
</code>
-
Ezek a hiányos for ciklus fejrészek szintaktikai hibát nem okoznak, de
+
A C# vektorai azonban önállóan is intelligens tárgyak (objektumok :). Egy C# vektortól mindíg meg lehet kérdezni,
-
mivel szintén ellentétben állnak a for ciklus lényegét leíró működésnek,
+
mekkora méretű. Erre a ''Length'' jellemzője (length=hossz) szolgál. Meg kell adni, melyik vektornak a méretét
-
használatuk stílustalan, nem javasolt.
+
kívánjuk megtudni. A vektor neve és a ''Length'' szó közé pontot teszünk. Az ''l.Length'' ebben a példában
 +
a vektor összes elemszámát (20) képviseli a while vezérlő feltételében.
 +
 
 +
<code lang="csharp">
 +
bool[] l = new bool[20];
 +
int i = 0;
 +
while (i<l.Length)
 +
{
 +
  t[i] = ...;
 +
  i++;
 +
}
 +
</code>
<alairas>Hernyák Zoltán</alairas>
<alairas>Hernyák Zoltán</alairas>
__NOTOC__
__NOTOC__

Aktuális változat


Vektorok

Gyakori probléma, hogy annyi adattal kell dolgoznunk, hogy kényelmetlen és zavaró lenne minden egyes adatnak saját változót deklarálni. Kényelmesebb lenne egyetlen változót használnunk, amely egy időben több adat tárolását is képes lenne megoldani. Az elv hasonló, mint a szemétkosár. Kényelmesebb összegyűjteni a szemétdarabkáinkat egy konténerbe, mint egyesével kezelni őket.

Ennek feltétele, hogy a sok (több) adat egyforma típusú legyen. A második félévben (OOP) látni fogjuk, hogy ennek gyakorlatilag semmi akadálya nincs, ha adataink alapvetően különböző típusúnak látszanak is, akkor is mindíg megoldható, hogy egyforma típusúnak látszódjanak (típuskompatibilitás).

Ebben a félévben azonban csak azzal az esettel foglalkozunk, amikor az adataink tényleg egyforma típusúak. Például egy adott időszak napi átlaghőmérséklet adatai. Ha az időszak két hónap, akkor körülbelül 60 darab double adattal kell egy időben dolgoznunk.

double a = 1.2;

A fenti kódban létrehoztunk egyetlen double tárolására képes változót. Az alábbi kódban létrehozunk egy 60 double tárolására képes változót:

double[] t;

A fenti t változó típusában (double[]) látszik, hogy itt nem egyetlen double, hanem több double adat összefogását kell elvégeznünk. A tényleges darabszám egyelőre nem látszik.

Összetett adattípusok

A fenti példában szereplő t változó alapvetően különbözik a korábban használt, egyszerű double változóktól. Az alaptípusának neve mögött szereplő két szögletes zárójel a tömb képző típusmódosító operátor jele. Jelenéte arra hívja fel a figyelmet, hogy nem egyetlen double, hanem sok double fog a t változóba kerülni.

Az ilyen típusú változókat, amelyek egy időben több érték tárolására is képesek - összetett típusú változóknak nevezzük. Ez a tulajdonságuk alapvetően megkülönbözteti őket az egyszerű típusoktól, amelybe eső változók egy időben csak egyetlen értéket tartalmazhatnak.

Műveletek

Nem kell persze nagy dolgokra számítani. Az összetett típusú változókkal jellemzően nem lehet műveletet végezni, valójában egy időben mindíg csak egyik elemükkel dolgozunk (egy érték). Ezen egy elemük már elemi típusú értékként viselkedik, tehát rá minden művelet alkalmazható, amit az adott egyszerű típusra megismertünk.

Jellemzés

Összetett adattípusokból is több van, őket különböző jellemzőik különböztetik meg egymástól:

  • tárolási kapacitás: ebből alapvetően két fajta van:
    • statikus kapacitás: az ilyen típusok képesek ugyan sok adat tárolására, de ezen adatok számát jó előre meg kell határozni. Az adott változó végig ugyanennyi adatot tárol, sem többet sem kevesebbet. Az adatok darabszámát a változó létrehozásakor rögzíteni kell.
    • dinamikus kapacitás: az ilyen változókban tárolt adatok száma időben változhat. Új adatot tehetünk be, korábban betett adatot később törölhetünk. A változó létrehozásakor esetleg megadható egy kezdeti darabszám, de ettől később bármikor eltérhetünk könnyedén. Előfordul az az eset is, amikor a változó létrehozásakor még nincs is benne egy adat sem (nulla elemszám).
  • memóriabeli elhelyezkedés: ez a jellemző azt mutatja, hogy a sok adat a memóriában hogyan helyezkedik el. Ezt a jellemzőt tárolási reprezentációnak is szokták nevezni:
    • folytonos: a tárolt adatok a memória egy összefüggő területén helyezkendnek el, általában szorosan egymás mellett, mögött.
    • szétszórt: a folytonos ellentéte, az egyes elemek egymástól akár nagyobb távolságra is elhelyezkedhetnek, nem jellemzi ezt a távolságot semmi, oda kerülnek, ahol szabad hely volt a memóriában abban a pillanatban.
  • típusosság: nem feltétlenül igaz, hogy a sok adat mindíg egyforma típusú, bár az adott összetett típus ezt kötelezővé teheti:
    • homogén: az ilyen összetett adattípus megköveteli, hogy az összes adat egyforma típusú legyen
    • inhomogén: ez esetben nem követelmény, hogy minden adat egyforma típusú legyen


  • adathozzáférési stratégia: ez egy bonyolult jellemző. azt mutatja meg, hogy amennyiben szükségünk van valamely, az összetett típusú változónkban tárolt n sorszámú adatra, akkor milyen lépéssorozat kell az érték előkereséséhez
    • direkt hozzáférés: a legjobb, ez esetben az adott adat sorszámának ismeretében az adat azonnal elérhető. Az adatelérés sebessége (ideje) nem az n értékétől függ (egyenletes sebesség)
    • szekvenciális: az n sorszámú adat eléréséhez minden előtte lévő elemet el kell érni (a feldolgozás az első adattal kezdődik, és halad sorban amíg el nem érjük az n sorszámút). Ekkor nyilvánvaló, hogy ha n kicsi, akkor gyorsabban hozzá tudunk férni az értékhez, mint nagyobb n esetekben
    • FIFO: speciálisan a sor (az ékezetek hiánya nem hiba) adatszerkezet jellemzője. Az adatok csakis abban a sorrendben kerülhetnek feldolgozásra, amilyen sorrendben az adatszerkezetbe bekerültek (first in first out)
    • LIFO: speciális a verem adatszerkezet jellemzője. Az adatok csakis fordított sorrendben dolgozhatók fel, mint amilyen sorrendben az adatszerkezetbe bekerültek (last in first out)

Vannak további, nagyon speciális adatelérések, mint az indexelt szekvenciális, a hash, a logaritmikus, stb. A fentiekről azért nem írunk részletesen, mert ez inkább az Algoritmusok és Adatszerkezetek tárgy témája.


Példák

A részletes tárgyalást mellőzve adunk egy listát, amely az egyes (az informatikában előforduló) összetett adatszerkezeteket jellemzi:

  • tömb: statikus, homogén, folytonos, direkt
  • lista: dinamikus, homogén, folytonos, direkt
  • láncolt lista: dinamikus, homogén, szétszórt, szekvenciális
  • verem: dinamikus, homogén, folytonos, fifo
  • sor: dinamikus, homogén, folytonos, lifo
  • rekord: statikus, inhomogén, folytonos, direkt
  • fa: dinamikus, homogén, szétszórt, (egyfajta) szekvenciális
  • szótár: dinamikus, homogén, folytonos v. szétszórt, hash

Vektorok

A vektorok speciális (egydimenziós) tömbök (igazából a tömb a fenti jellemzőkkel bíró típusok összefoglaló neve). Ezért a vektorok statikus, homogén, folytonos, direkt elérésű összetett adatszerkezetek.

Mivel homogének, létrehozásukkor meg kell adni az alaptípust. Ez szerepel a double[] típusnévben. A [] operátor jelzi, hogy nem egy double-ről beszélünk, hanem több double-ről.

A vektor statikus, vagyis induláskor meg kell adni a méretét. Ez nem szerepel a double[] típusnévben. Ez ugyanis absztrakt típusnév, az összes double alaptípusból álló vektor közös típusneve. Konkrét double vektor esetén konkrét méretet kell adni. Ezt a t változó kezdőértékének beállításakor kell közölni:

double[] t = new double[30];

Ebben az esetben a new operátor a memóriafoglalást jelöli (a konkrét méretű double vektornak konkrét mennyiségű memóriaterületet kell lefoglalni. Ebben a példában ez 30*8=240 byte. Ezt a new operátor az alaptípus helyigényéből (egy double 8 byte), és a méretből (30 darab double) önállóan számolja ki (valójában ezt az értéket a fordítóprogram fordításkor számolja ki, a generált kódban a new mögött már a konkrét 240 érték szerepel, a new-t ugyanis csak a méret érdekli, a felhasználási cél és mód már nem).

Amennyiben a fenti kezdőértékadást nem adjuk ki, a t változó nem képes egyetlen double adat tárolására sem. Az ilyen próbálkozásokat a fordítóprogram még fordításkor kiszúrja, és szintaktikai hibával leáll. Bár a vektor típusú változó deklarációja, és a helyfoglalás elválhat egymástól, de ritkán történik ilyen. Jellemzően a vektor deklarálásakor annak helyfoglalása is megtörténik.

double[] t;
...
t = new double[30];


Vektor feltöltése billentyűzetről

Egy (a példában szereplő) 30 elemű double vektorba 30 különböző double érték tárolható. A vektorok elemei induláskor típusuknak megfelelő nulla kezdőértékkel rendelkeznek. A szám (int, double,...) típusú vektorok ezért induláskor már 0-kal vannak feltöltve. Ettől eltérő értéket elhelyezhetünk a vektor valamely elemében egy értékadó utasítással. Ekkor meg kell mondani, melyik elembe kívánunk értéket írni. Az elemeket sorszámukkal azonosítjuk, a sorszámok 0-tól indulnak.

Egy 30 elemű double vektor elemeinek sorszáma 0,1,2,..,28,29. A sorszámok mindíg 0-tól indulnak, még akkor is, ha ez nekünk nem tetszik (idővel meg lehet szokni). Más programozási nyelveknél a szabály eltérő is lehet: a BASIC-ben pl a sorszámozás 1-től indul. Pascal-ban a sorszámozás (szinte) tetszőlegesen választható.


double[] t = new double[30];
int i = 0;
while (i<30)
{
  Console.Write("Kérek egy számot");
  double x = double.Parse( Console.ReadLine() );
  t[i] = x;
  i++;
}

A vektor valamely elemére hivatkozás során meg kell adni a vektor nevét (t), és a sorszámot (i), a sorszámot a vektor neve mögé írt szögletes zárójelpárba kell elhelyezni. A t[i] olvasata: a t vektor i. eleme, vagy a t vektor i sorszámú eleme.

A fenti esetben a tömbbe a billentyűzetről beírt számokat helyezzük el. Figyeljük meg, hogy a sorszámok tárolására, kezelésére az i változót használjuk. A sorszámok mindíg egész számok, egész a sorszámozást végző változónk mindíg int (vagy valamelyik testvére). A vektor első elemének sorszáma 0, ezért az i változónkba ezt az értéket helyezzük el induláskor. Az első bekért szám a t[0] pozícióra kerül. Az i++ miatt a sorszámunk lép egyet, és a következő bekért szám már a t[1] pozícióra kerül. Az i<30 miatt az utolsó sorszám, amire végrehajtódik a szám elhelyezés a vektorban az a t[29] pozíció. Ugyan az i ekkor is nő egyet, de i=30-ra már nem kerül végrehajtásra a ciklus.

Egy ilyen t vektornak 0..29 sorszámú elemei vannak. Egyéb sorszámú elemekre (kisebb v. nagyobb sorszámok használata) való hivatkozás futás közbeni hibát generál, és a program azon a ponton leáll. Mindíg nagyon gondoljuk át, nehogy ezt elvétsük!


Megjegyzés: a fenti kódban az x változóra igazából nincs szükség, a bekért értéket közvetlenül a vektorba is elhelyezhetjük:

double[] t = new double[30];
int i = 0;
while (i<30)
{
  Console.Write("Kérek egy számot");
  t[i] = double.Parse( Console.ReadLine() );
  i++;
}

Vektor feltöltése véletlen számokkal

A fenti megoldáshoz nagyon hasonló, amikor a vektort nem billentyűzetről, hanem véletlen számokkal töltjük fel:


double[] t = new double[30];
Random rnd = new Random();
int i = 0;
while (i<30)
{
  t[i] = rnd.Next(1,100);
  i++;
}

Vektor kiírása a képernyőre

Gyakran van arra szükség, hogy a vektorban lévő elemeket a képernyőre is kiírjuk. Ez szolgálhat ellenőrzési szerepet (látni akarjuk, hogy a vektorban tényleg benne vannak a számok, és melyek azok). Amikor a vektort véletlen számokkal töltjuk fel, természetes igény azokat meg is tekinteni.

...
int i = 0;
while (i<30)
{
  Console.WriteLine("{0}. sorszámú elem={1}",i,t[i]);
  i++;
}

Amikor sok elemről van szó, egy takarékosabb megoldást szoktunk választani: az elemeket a képernyőre egymás mellé, pl. vesszővel elválasztva írjuk ki:

...
int i = 0;
while (i<30)
{
  Console.Write("{0}, ",t[i]);
  i++;
}
Console.WriteLine();

A bezáró üres Console.WriteLine() szerepe, hogy az utolsóként kiírt vektorelem mögött álló kurzort a következő sor elejére rakja át, így megakadályozva, hogy a program következő kiírásai ugyanide kerüljenek.

Vektor mérete

A vektor mérete létrehozásakor adott. Ugyanakkor a vele dolgozó ciklusokban vezérlő feltételében is folyton előkerül. Amennyiben változik a vektor mérete, a ciklusok vezérlő feltételét is át kell(ene) írni. Erre a problémára két megoldás szokott születni.

Az első szerint a vektor méretét egy konstanban (vagy változóban) tároljuk el, és a ciklusokban is erre az értékre hivatkozunk:


const int N = 20;
bool[] l = new bool[N];
int i = 0;
while (i<N)
{
  t[i] = ...;
  i++;
}
Console.WriteLine("hány adatról van szó:");
int N = int.Parse( Console.ReadLine() );
bool[] l = new bool[N];
int i = 0;
while (i<N)
{
  l[i] = ...;
  i++;
}

Ez a megoldás szinte minden programozási nyelvben változatlan formában létezik. Néhány programozási nyelv (mint pl a PHP is) függvényt biztosít a vektor méretének utólagos lekérdezésére (ekkor ez a szám használható a vezérlő feltételben):

$l = array(2,3,5);
...
 
int i = 0;
while (i<sizeof($t))
{
  l[i] = ...;
  i++;
}

A C# vektorai azonban önállóan is intelligens tárgyak (objektumok :). Egy C# vektortól mindíg meg lehet kérdezni, mekkora méretű. Erre a Length jellemzője (length=hossz) szolgál. Meg kell adni, melyik vektornak a méretét kívánjuk megtudni. A vektor neve és a Length szó közé pontot teszünk. Az l.Length ebben a példában a vektor összes elemszámát (20) képviseli a while vezérlő feltételében.

bool[] l = new bool[20];
int i = 0;
while (i<l.Length)
{
  t[i] = ...;
  i++;
}
Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp1/page320
Nézetek
nincs sb_3.138.102.178 cikk