12. TANANYAG · FORRÁSKÓD
While és do…while
12_while_kommentelt.csAnimáció megnyitása →
namespace _12_While // A namespace (névtér) összefogja az ehhez a példához tartozó kódelemeket; ennek a névtérnek a neve _12_While.
{ // Itt kezdődik a névtér kódblokkja.
internal class Program // A Program nevű osztály deklarációja; ebben lesz a Main és a két saját bekérő metódus.
{ // Itt kezdődik a Program osztály törzse.
static void Main(string[] args) // A Main a program belépési pontja; static: példány nélkül futtatható, void: nem ad vissza értéket.
{ // Itt kezdődik a Main metódus törzse.
Random random = new Random(); // Egyetlen Random objektumot hozunk létre, és a program végéig ezt használjuk.
// 1. PÉLDA – while: ELÖLTESZTELŐ CIKLUS.
// Dobunk egy kockával, amíg 6-ost nem kapunk. Előre NEM tudjuk, hány dobás kell – ezért nem for ciklus.
int dobas = 0; // Ide kerül az aktuális dobás. A 0 biztosan nem 6, így a feltétel elsőre igaz lesz.
int dobasokSzama = 0; // Számláló: hány dobásra volt szükség.
while (dobas != 6) // A feltételt a ciklusmag ELŐTT vizsgálja: amíg a dobás nem 6, addig ismétel.
{ // Itt kezdődik a while ciklus törzse.
dobas = random.Next(1, 7); // Új dobás 1..6 között; a 7 már nem lehet eredmény.
dobasokSzama++; // Eggyel növeljük a számlálót.
Console.Write($"{dobas} "); // Kiírjuk a dobást ugyanabba a sorba.
} // A blokk végén a program visszaugrik a feltételhez, és újra ellenőrzi.
Console.WriteLine($"\n{dobasokSzama} dobás kellett a 6-oshoz.\n"); // Ide csak akkor jut el a program, ha a feltétel hamis lett, vagyis dobas == 6.
// 2. PÉLDA – while ÉS do…while KÜLÖNBSÉGE.
// Mindkét ciklus feltétele már az elején hamis. Melyik fut le, és hányszor?
int szam = 10; // A szam értéke 10, tehát a szam < 5 feltétel hamis.
while (szam < 5) // Elöltesztelő: a feltétel hamis, ezért a ciklusmag EGYSZER SEM fut le.
{
Console.WriteLine("while: ez a sor nem jelenik meg."); // Ez a sor soha nem hajtódik végre.
}
do // Hátultesztelő: a program előbb végrehajtja a ciklusmagot, és csak utána vizsgál.
{
Console.WriteLine("do…while: ez a sor egyszer megjelenik, pedig a feltétel hamis.\n"); // Ez a sor EGYSZER biztosan lefut.
} while (szam < 5); // A feltétel a ciklus VÉGÉN van; itt pontosvessző is kell!
// 3. PÉLDA – ELLENŐRZÖTT BEKÉRÉS: do…while + int.TryParse.
// A bekérést addig ismételjük, amíg a felhasználó nem ad meg érvényes számot a kért tartományban.
int kor; // Ebbe kerül a beolvasott életkor. Itt még nem kap értéket, a TryParse tölti fel.
bool sikeres; // Ebbe kerül, hogy sikerült-e számmá alakítani a beírt szöveget.
do // A bekérésnek legalább egyszer meg kell történnie, ezért hátultesztelő ciklus kell.
{
Console.Write("Hány éves vagy? (14–20): "); // Kiírjuk a kérdést; a Write után a kurzor ugyanabban a sorban marad.
sikeres = int.TryParse(Console.ReadLine(), out kor); // Megpróbálja számmá alakítani a beírt szöveget: true, ha sikerült, és ilyenkor a kor változóba kerül a szám.
if (!sikeres || kor < 14 || kor > 20) // Hibás, ha nem szám, VAGY túl kicsi, VAGY túl nagy.
{
Console.WriteLine("Hibás érték! 14 és 20 közötti egész számot adj meg!"); // A felhasználó megtudja, mit rontott el.
}
} while (!sikeres || kor < 14 || kor > 20); // Ugyanaz a feltétel: amíg hibás az adat, addig ismétlünk.
Console.WriteLine($"Rendben, {kor} éves vagy.\n"); // Ide már csak érvényes adattal jut el a program.
// 4. PÉLDA – VALÓS SZÁM BEKÉRÉSE PONTTAL ÉS VESSZŐVEL IS (metódusban).
// Pontosan ilyen feladat szerepel a technikusi vizsga konzolos részében.
double ertekeles = BekerValos(0, 10); // A metódus addig kérdez, amíg 0 és 10 közötti valós számot nem kap, és azt adja vissza.
Console.WriteLine($"A megadott értékelés: {ertekeles}\n"); // Kiírjuk a már biztosan érvényes valós számot.
// 5. PÉLDA – SZÁMKITALÁLÓ: A KÉT CIKLUS EGYÜTT.
// A gép gondol egy számra 1 és 100 között. A tippek ellenőrzött bekéréssel érkeznek.
int gondolt = random.Next(1, 101); // A gép által gondolt szám: 1..100.
int tipp = 0; // A 0 biztosan nem a gondolt szám, így a while feltétele elsőre igaz.
int tippekSzama = 0; // Számláló a tippek darabszámához.
while (tipp != gondolt) // Elöltesztelő ciklus: addig fut, amíg el nem találjuk a számot.
{
tipp = BekerEgesz("Tipped: ", 1, 100); // A metódus csak érvényes, 1..100 közötti egész számot ad vissza.
tippekSzama++; // Minden érvényes tipp után nő a számláló.
if (tipp < gondolt) // Ha a tipp kisebb a gondolt számnál...
{
Console.WriteLine("Nagyobb számra gondoltam."); // ...felfelé irányítjuk a játékost.
}
else if (tipp > gondolt) // Ha a tipp nagyobb a gondolt számnál...
{
Console.WriteLine("Kisebb számra gondoltam."); // ...lefelé irányítjuk a játékost.
}
} // Ha tipp == gondolt, a feltétel hamis lesz, és a ciklus véget ér.
Console.WriteLine($"Eltaláltad! A szám {gondolt} volt, {tippekSzama} tippből."); // A ciklus után már biztosan eltalálták a számot.
} // Itt ér véget a Main metódus törzse.
static int BekerEgesz(string kerdes, int minimum, int maximum) // Egész számot kér be a [minimum; maximum] tartományból; a kérdés szövegét paraméterként kapja.
{ // Itt kezdődik a BekerEgesz() metódus törzse.
int ertek; // Ide kerül a beolvasott szám.
bool sikeres; // Ide kerül, hogy sikerült-e a számmá alakítás.
do // Legalább egyszer meg kell kérdezni a felhasználót.
{
Console.Write(kerdes); // Kiírjuk a paraméterként kapott kérdést.
sikeres = int.TryParse(Console.ReadLine(), out ertek); // Szövegből egész szám – hiba esetén nem áll le a program, csak false-t kapunk.
if (!sikeres || ertek < minimum || ertek > maximum) // Hibás adat esetén...
{
Console.WriteLine($"Hibás érték! {minimum} és {maximum} közötti egész számot adj meg!"); // ...hibaüzenet, és a ciklus újra kérdez.
}
} while (!sikeres || ertek < minimum || ertek > maximum); // Amíg hibás az adat, addig ismétlünk.
return ertek; // Ide csak érvényes adattal jutunk el; ezt adjuk vissza a hívónak.
} // Itt ér véget a BekerEgesz() metódus.
static double BekerValos(double minimum, double maximum) // Valós számot kér be a [minimum; maximum] tartományból; a tizedespontot és a tizedesvesszőt is elfogadja.
{ // Itt kezdődik a BekerValos() metódus törzse.
double ertek; // Ide kerül a beolvasott valós szám.
bool sikeres; // Ide kerül, hogy sikerült-e a számmá alakítás.
do // Legalább egyszer meg kell kérdezni a felhasználót.
{
Console.Write($"Adj meg egy értékelést {minimum} és {maximum} között: "); // A kérdésben a tartomány is megjelenik.
string beirt = Console.ReadLine()!.Replace('.', ','); // Beolvassuk a szöveget, és minden pontot vesszőre cserélünk; így a "7.5" és a "7,5" is "7,5" lesz.
sikeres = double.TryParse(beirt, out ertek); // Magyar területi beállításnál a double.TryParse a tizedesvesszőt várja.
if (!sikeres || ertek < minimum || ertek > maximum) // Hibás adat esetén...
{
Console.WriteLine($"Hibás érték! {minimum} és {maximum} közötti számot adj meg!"); // ...hibaüzenet, és a ciklus újra kérdez.
}
} while (!sikeres || ertek < minimum || ertek > maximum); // Amíg hibás az adat, addig ismétlünk.
return ertek; // Csak érvényes valós számot adunk vissza.
} // Itt ér véget a BekerValos() metódus.
} // Itt ér véget a Program osztály törzse.
} // Itt ér véget a _12_While névtér.
/*
* MIRE VALÓ A while CIKLUS?
* -------------------------
*
* A for ciklust akkor használjuk, amikor előre tudjuk,
* hányszor kell ismételni:
*
* for (int i = 0; i < 10; i++) // pontosan 10-szer
*
* Sokszor azonban NEM tudjuk előre, hányszor kell ismételni:
*
* - dobunk, amíg 6-ost nem kapunk;
* - kérdezünk, amíg a felhasználó jó adatot nem ad;
* - tippelünk, amíg el nem találjuk a számot.
*
* Ilyenkor nem a darabszám, hanem egy FELTÉTEL dönti el,
* meddig fut a ciklus. Erre való a while és a do…while.
*
* Röviden:
*
* for → tudom, HÁNYSZOR
* while → tudom, MEDDIG
*/
/*
* A while CIKLUS FELÉPÍTÉSE
* -------------------------
*
* while (feltétel)
* {
* ciklusmag
* }
*
* A működése:
*
* 1. A program megvizsgálja a feltételt.
* 2. Ha IGAZ: végrehajtja a ciklusmagot, majd visszamegy az 1. lépéshez.
* 3. Ha HAMIS: kilép a ciklusból, és a ciklus utáni sorral folytatja.
*
* Mivel a feltétel a ciklusmag ELŐTT áll, ennek a neve:
*
* ELÖLTESZTELŐ CIKLUS
*
* Következmény: ha a feltétel már az elején hamis,
* a ciklusmag EGYSZER SEM fut le (lásd a 2. példát).
*
* Python megfelelője szinte betűre ugyanaz:
*
* Python C#
* ------ --
* while dobas != 6: while (dobas != 6)
* dobas = random.randint(1,6) {
* dobas = random.Next(1, 7);
* }
*
* A különbség csak a jelölésben van:
*
* - a feltétel zárójelbe kerül;
* - a ciklusmagot nem a behúzás, hanem a { } jelöli;
* - a sor végén nincs kettőspont.
*/
/*
* A FELTÉTELBEN SZEREPLŐ VÁLTOZÓNAK KELL KEZDŐÉRTÉK
* -------------------------------------------------
*
* Az 1. példában ezt írtuk:
*
* int dobas = 0;
* while (dobas != 6)
*
* Miért kell a = 0?
*
* Mert a while az első kör ELŐTT már megvizsgálja a dobas értékét.
* Ha a változónak nincs értéke, a C# fordító hibát jelez:
*
* Use of unassigned local variable 'dobas'
*
* Ezért olyan kezdőértéket adunk, amellyel a feltétel biztosan igaz,
* így a ciklus legalább egyszer elindul. A 0 jó választás,
* mert kockával 0-t nem lehet dobni.
*
* Ugyanezért kapja a tipp is a 0 kezdőértéket a számkitalálóban.
*/
/*
* A do…while CIKLUS FELÉPÍTÉSE
* ----------------------------
*
* do
* {
* ciklusmag
* } while (feltétel);
*
* A működése:
*
* 1. A program végrehajtja a ciklusmagot.
* 2. Megvizsgálja a feltételt.
* 3. Ha IGAZ: visszamegy az 1. lépéshez.
* 4. Ha HAMIS: kilép a ciklusból.
*
* Mivel a feltétel a ciklusmag UTÁN áll, ennek a neve:
*
* HÁTULTESZTELŐ CIKLUS
*
* Következmény: a ciklusmag LEGALÁBB EGYSZER biztosan lefut,
* akkor is, ha a feltétel már az elején hamis.
*
* Figyelem! A while (feltétel) után itt PONTOSVESSZŐ kell:
*
* } while (szam < 5);
*
* Pythonban NINCS do…while. Ott ezt így szoktuk megoldani:
*
* while True:
* ...
* if feltétel:
* break
*/
/*
* MIKOR while ÉS MIKOR do…while?
* ------------------------------
*
* A kérdés mindig ez:
*
* Előfordulhat-e, hogy a ciklusmagnak EGYSZER SEM kell lefutnia?
*
* IGEN → while
* NEM, legalább egyszer biztosan kell → do…while
*
* Példák:
*
* Feladat Ciklus
* ------- ------
* Dobás, amíg nem 6 while (vagy do…while)
* Tippelés, amíg el nem találja while (vagy do…while)
* Adat bekérése, amíg nem jó do…while
* Menü kiírása, amíg nem választ kilépést do…while
*
* Bekérésnél azért természetes a do…while, mert MIELŐTT
* ellenőrizni tudnánk az adatot, legalább egyszer meg kell kérdezni.
*
* Az 1. példa do…while ciklussal is megírható, sőt akkor nem kell
* a "trükkös" 0 kezdőérték sem:
*
* int dobas;
* do
* {
* dobas = random.Next(1, 7);
* } while (dobas != 6);
*
* Mindkét megoldás helyes. A lényeg, hogy tudd megindokolni,
* miért azt választottad.
*/
/*
* A FELTÉTEL: AMÍG ISMÉTELNI KELL, NEM AMÍG KÉSZ!
* -----------------------------------------------
*
* A while feltétele azt írja le, hogy MIKOR KELL MÉG folytatni,
* nem azt, hogy mikor kell megállni.
*
* A feladat szövege általában a megállást mondja:
*
* "Dobj, AMÍG 6-ost NEM kapsz." → megállás: dobas == 6
*
* A feltételbe ennek az ellentéte kerül:
*
* while (dobas != 6) → folytatás: dobas != 6
*
* Összetett feltételnél ugyanez a helyzet. Bekérésnél:
*
* jó az adat: sikeres ÉS kor >= 14 ÉS kor <= 20
* ismételni kell: !sikeres VAGY kor < 14 VAGY kor > 20
*
* Vagyis a "jó" feltétel tagadásakor:
*
* - minden rész az ellentétére fordul (== → !=, >= → <, <= → >);
* - az ÉS (&&) helyére VAGY (||) kerül.
*
* Ez a leggyakoribb hiba a while ciklusoknál: && kerül || helyett.
* Ha nem vagy biztos benne, írd így, ez is helyes:
*
* while (!(sikeres && kor >= 14 && kor <= 20));
*/
/*
* VÉGTELEN CIKLUS
* ---------------
*
* Ha a ciklusmagban semmi nem változtatja meg a feltételt,
* a ciklus soha nem áll le:
*
* int dobas = 0;
* while (dobas != 6)
* {
* Console.WriteLine("Dobok..."); // a dobas soha nem változik!
* }
*
* Ezért minden while ciklusnál nézd meg:
*
* Melyik sor változtatja meg a ciklusmagban azt,
* amit a feltétel vizsgál?
*
* Ha ilyen sor nincs, a ciklus végtelen.
*
* Futó végtelen ciklust a konzolablakban a Ctrl + C billentyűkkel
* lehet leállítani.
*/
/*
* int.TryParse – BIZTONSÁGOS SZÁMMÁ ALAKÍTÁS
* ------------------------------------------
*
* Eddig így kértünk be számot:
*
* int kor = int.Parse(Console.ReadLine());
*
* Ha a felhasználó azt írja be, hogy "alma", az int.Parse
* hibát dob, és a program LEÁLL:
*
* System.FormatException: The input string 'alma' was not
* in a correct format.
*
* Az int.TryParse ezzel szemben nem állítja le a programot.
* Megpróbálja az átalakítást, és két dolgot ad vissza:
*
* bool sikeres = int.TryParse(Console.ReadLine(), out kor);
* ↓ ↓ ↓
* sikerült-e? ezt próbálja számmá ide teszi
* (true / false) alakítani a számot
*
* Esetek:
*
* Beírt szöveg sikeres kor
* ------------ ------- ---
* "17" true 17
* "alma" false 0
* "" false 0
* "17,5" false 0 (nem egész szám!)
*
* Az out kulcsszó jelzi, hogy a metódus ebbe a változóba ÍR.
* Egy metódus normál esetben csak egy értéket tud visszaadni (return),
* az out paraméterrel egy második eredményt is "kiad".
*
* Python megfelelője:
*
* Python C#
* ------ --
* try: bool sikeres =
* kor = int(input()) int.TryParse(Console.ReadLine(), out kor);
* except ValueError: if (!sikeres)
* print("Hibás!") Console.WriteLine("Hibás!");
*
* Pythonban a hibát utólag "elkapjuk", C#-ban a TryParse
* előre megmondja, hogy sikerült-e. (A C#-nak is van try–catch
* szerkezete, azzal később foglalkozunk.)
*/
/*
* AZ ELLENŐRZÖTT BEKÉRÉS MINTÁJA – EZT TUDD FEJBŐL!
* -------------------------------------------------
*
* int ertek;
* bool sikeres;
* do
* {
* Console.Write("Kérdés: ");
* sikeres = int.TryParse(Console.ReadLine(), out ertek);
* } while (!sikeres || ertek < MIN || ertek > MAX);
*
* Három rész:
*
* 1. do → legalább egyszer kérdezünk;
* 2. TryParse → nem áll le a program hibás szövegnél;
* 3. feltétel → ismétlünk, ha nem szám VAGY tartományon kívül esik.
*
* A feltétel sorrendje számít: a !sikeres áll elöl.
* Ha az átalakítás nem sikerült, a || miatt a többi részt
* a program már meg sem vizsgálja.
*
* Ha hibaüzenetet is szeretnénk, a ciklusmagba egy if kerül
* ugyanazzal a feltétellel (lásd a 3. példát).
*
* Érdemes az egészet metódusba tenni (BekerEgesz), így a programban
* akárhányszor használhatjuk egyetlen sorral:
*
* int tipp = BekerEgesz("Tipped: ", 1, 100);
* int ev = BekerEgesz("Születési év: ", 1900, 2026);
*/
/*
* TIZEDESPONT VAGY TIZEDESVESSZŐ?
* -------------------------------
*
* Magyar területi beállítású gépen a double.TryParse a
* TIZEDESVESSZŐT várja:
*
* "7,5" → sikeres, 7,5
* "7.5" → NEM az, amit szeretnénk
*
* A felhasználók viszont gyakran pontot írnak. Ezért a beolvasott
* szövegben a pontokat vesszőre cseréljük, MIELŐTT számmá alakítjuk:
*
* string beirt = Console.ReadLine()!.Replace('.', ',');
*
* A Replace('.', ',') a szöveg minden '.' karakterét ','-re cseréli.
* Így mindkét írásmód ugyanazt adja:
*
* "7.5" → "7,5" → 7,5
* "7,5" → "7,5" → 7,5
*
* A ! jel a ReadLine() után azt mondja a fordítónak: "tudom,
* hogy itt nem lesz null érték". Enélkül a Visual Studio
* figyelmeztetést (zöld/sárga hullámvonalat) mutatna.
*
* A technikusi vizsga konzolos részében szó szerint ilyen feladat
* szerepelt:
*
* "A bekérést mindaddig ismételje meg, amíg a felhasználó egy
* [0;10] intervallumba eső valós számot nem ad meg. (...)
* Biztosítsa, hogy a program a tizedesvesszővel és a
* tizedesponttal megadott számokat is elfogadja."
*
* Ez a BekerValos(0, 10) metódus.
*/
/*
* ÖSSZEFOGLALÁS
* -------------
*
* for ismert darabszám for (int i = 0; i < n; i++)
* while elöltesztelő 0-szor is lefuthat
* do…while hátultesztelő legalább 1-szer lefut, ; a végén!
*
* int.TryParse(szöveg, out valtozo) true / false + a szám
* double.TryParse(szöveg, out valtozo) ugyanez valós számra
* .Replace('.', ',') pont helyett vessző
*
* Ellenőrző kérdések:
*
* 1. Hányszor fut le a ciklusmag?
* int x = 3;
* while (x > 5) { x++; }
*
* 2. És ha do…while-ra írjuk át?
*
* 3. Mi a hiba?
* int dobas = 0;
* while (dobas != 6)
* {
* Console.WriteLine(dobas);
* }
*
* 4. Írd fel annak a bekérésnek a feltételét, amely egy 1 és 5
* közötti osztályzatot fogad el!
*
* 5. Mi lesz a sikeres és a szam értéke?
* bool sikeres = int.TryParse("12a", out int szam);
*/
namespace _12_While
{
internal class Program
{
static void Main(string[] args)
{
Random random = new Random();
int dobas = 0;
int dobasokSzama = 0;
while (dobas != 6)
{
dobas = random.Next(1, 7);
dobasokSzama++;
Console.Write($"{dobas} ");
}
Console.WriteLine($"\n{dobasokSzama} dobás kellett a 6-oshoz.\n");
int szam = 10;
while (szam < 5)
{
Console.WriteLine("while: ez a sor nem jelenik meg.");
}
do
{
Console.WriteLine("do…while: ez a sor egyszer megjelenik, pedig a feltétel hamis.\n");
} while (szam < 5);
int kor;
bool sikeres;
do
{
Console.Write("Hány éves vagy? (14–20): ");
sikeres = int.TryParse(Console.ReadLine(), out kor);
if (!sikeres || kor < 14 || kor > 20)
{
Console.WriteLine("Hibás érték! 14 és 20 közötti egész számot adj meg!");
}
} while (!sikeres || kor < 14 || kor > 20);
Console.WriteLine($"Rendben, {kor} éves vagy.\n");
double ertekeles = BekerValos(0, 10);
Console.WriteLine($"A megadott értékelés: {ertekeles}\n");
int gondolt = random.Next(1, 101);
int tipp = 0;
int tippekSzama = 0;
while (tipp != gondolt)
{
tipp = BekerEgesz("Tipped: ", 1, 100);
tippekSzama++;
if (tipp < gondolt)
{
Console.WriteLine("Nagyobb számra gondoltam.");
}
else if (tipp > gondolt)
{
Console.WriteLine("Kisebb számra gondoltam.");
}
}
Console.WriteLine($"Eltaláltad! A szám {gondolt} volt, {tippekSzama} tippből.");
}
static int BekerEgesz(string kerdes, int minimum, int maximum)
{
int ertek;
bool sikeres;
do
{
Console.Write(kerdes);
sikeres = int.TryParse(Console.ReadLine(), out ertek);
if (!sikeres || ertek < minimum || ertek > maximum)
{
Console.WriteLine($"Hibás érték! {minimum} és {maximum} közötti egész számot adj meg!");
}
} while (!sikeres || ertek < minimum || ertek > maximum);
return ertek;
}
static double BekerValos(double minimum, double maximum)
{
double ertek;
bool sikeres;
do
{
Console.Write($"Adj meg egy értékelést {minimum} és {maximum} között: ");
string beirt = Console.ReadLine()!.Replace('.', ',');
sikeres = double.TryParse(beirt, out ertek);
if (!sikeres || ertek < minimum || ertek > maximum)
{
Console.WriteLine($"Hibás érték! {minimum} és {maximum} közötti számot adj meg!");
}
} while (!sikeres || ertek < minimum || ertek > maximum);
return ertek;
}
}
}