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)
 
(4 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
+
= Összetett adattípusok =
-
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>
+
 +
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.
 +
 
 +
= 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:
<code lang="csharp">
<code lang="csharp">
-
for(int i=0; i<10; i++)
+
double[] t = new double[30];
-
{
+
-
  Console.Write(i);
+
-
}
+
</code>
</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
+
Ebben az esetben a ''new'' operátor a memóriafoglalást jelöli (a konkrét méretű double vektornak konkrét mennyiségű
-
''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.
+
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
-
A ciklusmag minden lefutása után végrehajtódik az inkrementáló utasítás, vagyis az ''i'' értéke nő 1-el.
+
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).
-
A példában szereplő for ciklus fejrészében minden információ szerepel, melyből meghatározható, hogy
+
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
-
ez a for ciklus pontosan 10-szer fog lefutni. A ciklusmagban az ''i'' változó értéke kiolvasható,  
+
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ó
-
de megváltoztatása tilos. Ennek oka, hogy az ''i'' változó értékének átírása a pontos 10 ismétlést
+
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
-
módosítaná (vagy kevesebbszer, vagy többször kerülne végrehajtásra).  
+
helyfoglalása is megtörténik.
-
<box type="warn">A ciklusváltozó ciklusmagon belüli módosítása nem szintaktikai, hanem elvi hiba. A fordító
+
<code lang="csharp">
-
eltűri, de ezen tevékenység ellentmond a for ciklus lényegének. Akinek ilyenre van szüksége, az használjon
+
double[] t;
-
while ciklust.</box>
+
...
 +
t = new double[30];
 +
</code>
-
= Példa =
 
-
Hibás tehát az alábbi példa (bár nem szintaktikailag, hanem elvi, stílusbeli hiba). A programban
+
= Vektor feltöltése billentyűzetről =
-
a 10 elemű tömböt csak pozitív számokkal kívánjuk feltölteni. Amennyiben a program kezelője valamely
+
 
-
elem értékének negatív számot kíván megadni, azt az elemet újra bekérjük:
+
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)
{
{
-
   Console.Write("{0}. szám értéke:");
+
   Console.Write("Kérek egy számot");
-
   tomb[i] = int.Parse(Console.ReadLine());
+
   double x = double.Parse( Console.ReadLine() );
-
   if (tomb[i]<0) i--;
+
   t[i] = x;
 +
  i++;
}
}
</code>
</code>
-
A feltételben az ''i--'' utasítás az ''i'' értékét csökkenti 1-el, amit
+
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
-
az inkrementáló utasítás (''i++'') meg fog növelni 1-el. E miatt negatív
+
a vektor neve mögé írt szögletes zárójelpárba kell elhelyezni. A ''t[i]'' olvasata: ''a t vektor i. eleme'', vagy
-
szám beírása esetén az ''i'' értéke gyakorlatilag nem fog változni - a ciklus
+
''a t vektor i sorszámú eleme''.
-
következő lefutásakor is ugyanazon ''i'' tömbelem kerül feltöltésre.
+
-
Ez elegánsabban megoldható while ciklussal:
+
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.
 +
 
 +
<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>
 +
 
 +
''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[] tomb = new tomb[10];
+
double[] t = new double[30];
-
int i=0;
+
int i = 0;
-
while (i<10)
+
while (i<30)
{
{
-
   Console.Write("{0}. szám értéke:");
+
   Console.Write("Kérek egy számot");
-
   tomb[i] = int.Parse(Console.ReadLine());
+
   t[i] = double.Parse( Console.ReadLine() );
-
   if (tomb[i]>0) i++;
+
   i++;
}
}
</code>
</code>
-
Vagyis csak akkor lépünk a következő tömbelem bekérésére, ha ezen tömbelemmel
+
= Vektor feltöltése véletlen számokkal =
-
elégedettek vagyunk.
+
-
Másik lehetséges megoldás:
+
A fenti megoldáshoz nagyon hasonló, amikor a vektort nem billentyűzetről, hanem véletlen számokkal töltjük fel:
<code lang="csharp">
<code lang="csharp">
-
int[] tomb = new tomb[10];
+
double[] t = new double[30];
-
for(int i=0;i<10;i++)
+
Random rnd = new Random();
 +
int i = 0;
 +
while (i<30)
{
{
-
   do
+
   t[i] = rnd.Next(1,100);
-
  {
+
   i++;
-
    Console.Write("{0}. szám értéke:");
+
-
    tomb[i] = int.Parse(Console.ReadLine());
+
-
   }
+
-
  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
+
= Vektor kiírása a képernyőre =
-
annak értékével elégedettek nem vagyunk.
+
-
= Trükkösebb ciklusok (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
 +
(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.
-
A for ciklus fejrészében szereplő vezérlő feltétel tetszőlegesen összetett lehet,
+
<code lang="csharp">
-
hiszen az gyakorlatilag megfelel a while ciklus vezérlő feltételének szerepkörével:
+
...
 +
int i = 0;
 +
while (i<30)
 +
{
 +
  Console.WriteLine("{0}. sorszámú elem={1}",i,t[i]);
 +
  i++;
 +
}
 +
</code>
 +
 
 +
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 db=0;
+
...
-
for(int i=0;i<10 && db<3; i++)
+
int i = 0;
-
   if (tomb[i]>0) db++;
+
while (i<30)
 +
{
 +
   Console.Write("{0}, ",t[i]);
 +
  i++;
 +
}
 +
Console.WriteLine();
</code>
</code>
-
Ez a for ciklus legkésőbb 10 menetben leáll. Korábban is leállhat, ha
+
A bezáró üres ''Console.WriteLine()'' szerepe, hogy az utolsóként kiírt vektorelem mögött álló kurzort
-
a tömbben találtunk 3 pozitív számot.
+
a következő sor elejére rakja át, így megakadályozva, hogy a program következő kiírásai ugyanide kerüljenek.
-
Ez a működés némiképp ellentmond a korábban említett elvvel: a for ciklus
+
= Vektor mérete =
-
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) =
+
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:
-
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) { ... }
+
const int N = 20;
-
for(int i=10;i>0; i-- ) { ... }
+
bool[] l = new bool[N];
-
for(int i=2;i<100; i=i+2 ) { ... }
+
int i = 0;
-
for(int i=2;i<100; i=i*2-1 ) { ... }
+
while (i<N)
 +
{
 +
  t[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
 
-
= 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());
+
Console.WriteLine("hány adatról van szó:");
-
for(  ;i<10; i++)  
+
int N = int.Parse( Console.ReadLine() );
-
{
+
bool[] l = new bool[N];
-
    ...  
+
int i = 0;
-
}
+
while (i<N)
 +
{
 +
  l[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
A for ciklus fejrésze ekkor is három részből kell álljon! Vagyis az üres
+
Ez a megoldás szinte minden programozási nyelvben változatlan formában létezik. Néhány programozási nyelv (mint
-
utasítást is le kell zárni a pontosvesszővel.
+
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):
-
<code lang="csharp">
+
<code lang="php">
-
int i=0;
+
$l = array(2,3,5);
-
for( ;i<10; )  
+
...
-
{
+
 
-
    ...  
+
int i = 0;
-
    i++;
+
while (i<sizeof($t))
-
}
+
{
 +
  l[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
Ebben a példában szereplő for ciklus fejrészéből már mindkét elhagyható
+
A C# vektorai azonban önállóan is intelligens tárgyak (objektumok :). Egy C# vektortól mindíg meg lehet kérdezni,
-
utasítás elhagyásra is került. Ez már gyakorlatilag megfelel a korábban
+
mekkora méretű. Erre a ''Length'' jellemzője (length=hossz) szolgál. Meg kell adni, melyik vektornak a méretét
-
felírt while ciklus működésének.
+
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">
<code lang="csharp">
-
for(int i=0;i<10; )  
+
bool[] l = new bool[20];
-
{
+
int i = 0;
-
    ...  
+
while (i<l.Length)
-
    i++;
+
{
-
}
+
  t[i] = ...;
 +
  i++;
 +
}
</code>
</code>
-
 
-
Ezek a hiányos for ciklus fejrészek szintaktikai hibát nem okoznak, de
 
-
mivel szintén ellentétben állnak a for ciklus lényegét leíró működésnek,
 
-
használatuk stílustalan, nem javasolt.
 
<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.142.197.198 cikk