03. MODUL · PYTHON

Ciklusok.

for és while – ismétlés a programban

1. fejezet

Miért kell ciklus?

Írd ki a számokat 1-től 5-ig. Ez még megy öt print-tel. És 1-től 1000-ig? Vagy annyiszor, ahányat a felhasználó kér? Az ilyen feladatokhoz ciklus kell: egy utasításcsoport, amelyet a program többször egymás után végrehajt.

Ciklus, ciklusmag, ismétlés

A ciklus egy kódblokk ismételt végrehajtása. A megismételt, beljebb kezdett sorok a ciklusmag; a mag egy-egy lefutása egy ismétlés (iteráció). A Pythonban kétféle ciklus van: a for egy sorozat elemein halad végig, a while addig ismétel, amíg egy feltétel igaz.

for i in range(1, 6):
    print(i, "a négyzete:", i * i)
print("Kész.")

Kövesd Lépésenként. A Memória részben figyeld az i változót: minden körben a következő értéket kapja. A „Kész.” sor nincs beljebb kezdve, ezért a ciklus után, egyszer fut le.

2. fejezet

A for ciklus

for valtozo in sorozat:
    ciklusmag   # minden elemre egyszer lefut
  • forkulcsszó: „minden … -ra/-re”
  • valtozoa ciklusváltozó: körönként a sorozat következő eleme
  • in sorozatamin végighaladunk: range(), szöveg, lista…
  • : + behúzásutána jön a ciklusmag

A for annyiszor futtatja le a magot, ahány eleme a sorozatnak van, és minden körben a ciklusváltozóba teszi a soron következő elemet. A sorozat lehet például egy szöveg – ilyenkor a karaktereken megy végig:

szo = input("Adj meg egy szót: ")
for betu in szo:
    print(betu)

…vagy egy lista (a listákról a 4. modulban lesz bővebben szó; itt elég annyi, hogy szögletes zárójelben, vesszővel felsorolt értékek):

for gyumolcs in ["alma", "körte", "szilva"]:
    print("Kosárba tettem:", gyumolcs)

3. fejezet

Számsorozatok: range()

A leggyakoribb sorozat egy számtartomány. A range() függvény egy, két vagy három egész számot kap:

HívásSzámokJelentés
range(5)0 1 2 3 40-tól 5-ig, az 5 már nem
range(1, 6)1 2 3 4 51-től 6-ig, a 6 már nem
range(0, 20, 5)0 5 10 15ötösével
range(10, 0, -2)10 8 6 4 2visszafelé, kettesével

A felső határ (stop) sosem része a sorozatnak. A range(1, 6) öt számot ad, 1-től 5-ig. Ennek az az előnye, hogy a range(n) pontosan n számot ad (0-tól n−1-ig), ezért „csináld meg n-szer” jelentésben is használható.

for mp in range(10, 0, -1):
    print(mp, end=" ")
print("Start!")

Mit ír ki?

for i in range(2, 11, 3):
    print(i, end=" ")
  1. 2 5 8 11
  2. 2 5 8
  3. 3 6 9

2-től indul, hármasával lép: 2, 5, 8. A következő a 11 lenne, de a stop (11) már nem része a sorozatnak.

4. fejezet

Ciklusminták: összegzés, számlálás, szélsőérték

A ciklusos feladatok nagy része néhány visszatérő mintára épül. Ha ezeket felismered, a legtöbb feladat megoldása már csak összerakás.

Összegzés

Egy gyűjtőváltozó a ciklus előtt 0-t kap, és a ciklusmagban minden körben hozzáadjuk az aktuális értéket. A osszeg += szam az osszeg = osszeg + szam rövidítése. Ugyanígy működik a -=, *=, /=, //= és %= is: például az x //= 10 jelentése x = x // 10.

n = int(input("Meddig adjam össze a számokat? "))
osszeg = 0
for szam in range(1, n + 1):
    osszeg += szam
print(f"1 + 2 + … + {n} = {osszeg}")

Kövesd lépésenként n = 5-tel: az osszeg értéke 0 → 1 → 3 → 6 → 10 → 15. Figyeld meg a range(1, n + 1)-et: azért kell a + 1, mert a stop nem része a sorozatnak, mi pedig az n-et is össze akarjuk adni.

A gyűjtőváltozó kezdőértéke a ciklus előtt álljon. Ha az osszeg = 0 a ciklusmagba kerül, minden körben lenullázódik, és a végén csak az utolsó szám marad benne. Próbáld ki: tedd beljebb a sort, és kövesd lépésenként.

Számlálás

A számláló is a ciklus előtt kap 0-t, és csak akkor nő eggyel, ha az elem megfelel a feltételnek – ehhez a ciklusmagban egy if kell.

szoveg = input("Írj egy mondatot: ")
db = 0
for betu in szoveg.lower():
    if betu in "aáeéiíoóöőuúüű":
        db += 1
print("Magánhangzók száma:", db)

Két új apróság a példában: a szoveg.lower() a szöveg kisbetűs változatát adja (így a nagy kezdőbetű is magánhangzónak számít), a betu in "aáeé…" pedig igaz, ha a karakter szerepel a felsorolt szövegben – szövegnél az in azt vizsgálja, hogy a bal oldal része-e a jobb oldalnak.

Szélsőérték (legnagyobb, legkisebb)

A legnagyobb érték kereséséhez az első elemet tekintjük kezdetben a legnagyobbnak, majd minden további elemet összevetünk vele. Ha nagyobbat találunk, az lesz az új „eddigi legnagyobb”.

db = int(input("Hány számot adsz meg? "))
legnagyobb = int(input("1. szám: "))
for i in range(2, db + 1):
    szam = int(input(f"{i}. szám: "))
    if szam > legnagyobb:
        legnagyobb = szam
print("A legnagyobb:", legnagyobb)

Miért nem 0 a legnagyobb kezdőértéke? Mert ha minden szám negatív, a 0 „győzne”, pedig nincs is a számok között. Az első tényleges elem mindig biztonságos kiindulópont.

5. fejezet

A while ciklus

while feltétel:
    ciklusmag   # addig ismétlődik, amíg a feltétel igaz

A while minden kör előtt kiértékeli a feltételt: ha igaz, lefut a mag, és utána újra a feltétel következik; ha hamis, a ciklus véget ér. Ha a feltétel már az elején hamis, a mag egyszer sem fut le. Akkor használjuk, amikor nem tudjuk előre, hány kör kell – például addig kérdezünk, amíg jó választ nem kapunk.

szam = 1
while szam <= 5:
    print(szam)
    szam += 1
print("A ciklus után szam =", szam)

Kövesd lépésenként: a Kiértékelés minden körben megmutatja a feltétel aktuális értékét. A ciklus utáni sorban a szam már 6 – éppen ez tette hamissá a feltételt.

Mit ír ki?

n = 0
db = 0
while n < 10:
    n += 3
    db += 1
print(n, db)
  1. 9 3
  2. 12 4
  3. 10 4

Az n értékei: 3, 6, 9, majd 12. A 9 után a feltétel (9 < 10) még igaz, ezért lefut egy negyedik kör is, és csak a 12-nél válik hamissá. A feltételt mindig a kör elején vizsgálja a Python – a „túllépés” a ciklus után látszik.

Példa: jelszó

jelszo = ""
while jelszo != "titok":
    jelszo = input("Jelszó: ")
print("Beléptél.")

Az első sor (jelszo = "") azért kell, mert a feltétel kiértékelésekor a változónak már léteznie kell. Az üres szöveg biztosan nem egyezik a jelszóval, így legalább egyszer bekérjük.

Példa: végjelig olvasás

Gyakori feladat, hogy a felhasználó tetszőleges számú adatot ad meg, a végét pedig egy megbeszélt végjel (pl. 0) jelzi. A végjelet már nem dolgozzuk fel – ezért a beolvasás a ciklus előtt és a mag végén áll:

osszeg = 0
db = 0
szam = int(input("Szám (0 = vége): "))
while szam != 0:
    osszeg += szam
    db += 1
    szam = int(input("Szám (0 = vége): "))
print(f"{db} számot adtál meg, az összegük {osszeg}.")

Végtelen ciklus. Ha a ciklusmag sosem teszi hamissá a feltételt, a ciklus soha nem ér véget. Az alábbi programból lemaradt a szam += 1. Futtasd: a tananyag felismeri és leállítja – a valódi gépen a terminálban Ctrl+C állítja meg. Javítsd ki.

szam = 1
while szam <= 5:
    print(szam)

for vagy while?

Ha előre tudod, hányszor kell ismételni, vagy egy sorozat elemein mész végig: for. Ha egy feltétel teljesüléséig kell ismételni, és a körök száma a futás közben derül ki: while. Minden for átírható while-ra (számlálóval), de a for rövidebb, és nem lehet elfelejteni benne a léptetést.

6. fejezet

break és continue

A break azonnal kilép a ciklusból (a legbelsőből), a continue pedig a mag hátralévő részét kihagyja, és a következő körrel folytatja. Mindkettő általában egy if belsejében áll.

for i in range(1, 11):
    if i % 3 == 0:
        continue
    if i > 7:
        break
    print(i, end=" ")
print()

A 3, 6 kimarad (continue), a 8-nál pedig a ciklus véget ér (break), így a 9 és a 10 már nem kerül sorra.

A while True + break minta

Bemenet ellenőrzésénél kényelmes, ha a ciklus „örökké” fut, és a jó adat beolvasása után break-kel lépünk ki. Így a beolvasás csak egyszer szerepel a kódban:

while True:
    jegy = int(input("Érdemjegy (1–5): "))
    if 1 <= jegy <= 5:
        break
    print("Hibás érték, próbáld újra!")
print("Rögzítve:", jegy)

7. fejezet

Beágyazott ciklusok

Egy ciklus magjában újabb ciklus is állhat. A belső ciklus a külső minden egyes körében végigfut. Táblázatokhoz, rácsokhoz, alakzatok rajzolásához használjuk.

for sor in range(1, 6):
    for oszlop in range(1, 6):
        print(f"{sor * oszlop:4}", end="")
    print()

A belső ciklus egy sort ír ki egymás mellé (end=""), a sor végén a külső ciklus print()-je kezd új sort. A {…:4} formázás minden számot 4 karakter szélesre igazít, így az oszlopok egymás alá kerülnek. Összesen 5 · 5 = 25-ször fut le a belső mag.

Alakzatok: háromszög és fenyőfa

A "*" * db szövegismétlés sokszor kiváltja a belső ciklust. A fenyőfa i-edik sorában (az i 0-tól indul) magassag - i - 1 szóköz és 2 * i + 1 csillag áll – rajzold le papíron egy 4 magas fára, és ellenőrizd a képletet.

magassag = int(input("A fa magassága: "))
for i in range(magassag):
    print(" " * (magassag - i - 1) + "*" * (2 * i + 1))
for i in range(2):
    print(" " * (magassag - 2) + "|||")

Mit ír ki?

db = 0
for i in range(3):
    for j in range(4):
        db += 1
print(db)
  1. 7
  2. 4
  3. 12
  4. 3

A külső ciklus 3-szor fut, és mindegyik körben a belső ciklus 4-szer: 3 · 4 = 12 növelés.

8. fejezet

Összefoglaló példa: számkitaláló játék

A gép gondol egy számra 1 és 100 között, a játékos legfeljebb 7-szer tippelhet, és minden tipp után megtudja, hogy a gondolt szám kisebb vagy nagyobb. A véletlenszámot a random modul randint() függvénye adja – a modult az 5. modulban részletesen megismered.

import random

gondolt = random.randint(1, 100)
max_tipp = 7
tippek = 0
eltalalta = False

print("Gondoltam egy számra 1 és 100 között.")
while tippek < max_tipp and not eltalalta:
    tipp = int(input(f"{tippek + 1}. tipp: "))
    tippek += 1
    if tipp == gondolt:
        eltalalta = True
    elif tipp < gondolt:
        print("A gondolt szám nagyobb.")
    else:
        print("A gondolt szám kisebb.")

if eltalalta:
    print(f"Eltaláltad {tippek} tippből!")
else:
    print(f"Elfogytak a tippek. A szám {gondolt} volt.")

A ciklusnak két kilépési oka lehet: elfogytak a tippek, vagy eltalálta. Az eltalalta logikai változó (más néven jelző) őrzi meg, melyik történt – a ciklus után ennek alapján írjuk ki az eredményt. Ha ügyesen felezed a tartományt (50, 25 vagy 75, …), 7 tipp mindig elég – miért?

Miért elég 7 tipp?

Minden jól választott tipp (a még lehetséges tartomány közepe) legalább a felére szűkíti a lehetőségeket. 7 felezés után 100 lehetőségből legfeljebb 1 marad, mert 27 = 128 ≥ 100. Ezt az eljárást bináris keresésnek nevezzük.

9. fejezet

Gyakorló feladatok

Szorzótábla

Kérj be egy egész számot, és írd ki for ciklussal a szorzótábláját 1-től 10-ig:

Szám: 7
1 * 7 = 7
2 * 7 = 14
…
10 * 7 = 70
szam = int(input("Szám: "))
for i in range(1, 11):
    print(f"{i} * {szam} = {i * szam}")
Tipp

for i in range(1, 11): – a 11 kell, hogy a 10 is benne legyen.

Páros számok egy sorban

Kérj be egy pozitív egész számot (határ), és írd ki egy sorban, szóközzel elválasztva a 2 és a határ közötti páros számokat (a határt is, ha páros):

Határ: 10
2 4 6 8 10
hatar = int(input("Határ: "))
for szam in range(2, hatar + 1, 2):
    print(szam, end=" ")
print()
Tipp

Kettesével lépő range: range(2, hatar + 1, 2). Egy sorba íráshoz: print(szam, end=" "), a végén pedig egy üres print() zárja a sort. (A sor végi szóköz nem számít hibának.)

1-től n-ig

Kérj be egy pozitív egész n-t, és írd ki ciklussal az 1 + 2 + … + n összeget, valamint a négyzetszámok összegét (1² + 2² + … + n²):

n = 100
Összeg: 5050
Négyzetösszeg: 338350
n = int(input("n = "))
osszeg = 0
negyzetosszeg = 0
for i in range(1, n + 1):
    osszeg += i
    negyzetosszeg += i * i
print("Összeg:", osszeg)
print("Négyzetösszeg:", negyzetosszeg)
Tipp

Két gyűjtőváltozó kell, mindkettő 0-ról indul a ciklus előtt. Ellenőrzésnek: Gauss képlete szerint az összeg n·(n+1)/2.

Számjegyek összege

Kérj be egy tetszőleges hosszú, nemnegatív egész számot, és írd ki a számjegyei összegét. Szövegművelet nélkül, while ciklussal, % és // segítségével dolgozz.

Szám: 90817
A számjegyek összege: 25
szam = int(input("Szám: "))
osszeg = 0
while szam > 0:
    osszeg += szam % 10
    szam //= 10
print("A számjegyek összege:", osszeg)
Tipp

A szam % 10 az utolsó számjegy; a szam //= 10 levágja azt. Ismételd, amíg a szám nagyobb 0-nál. Kövesd lépésenként a mintamegoldást.

Jegyek átlaga végjelig

Olvass be érdemjegyeket, amíg a felhasználó 0-t nem ír be. A végén írd ki a jegyek számát és az átlagot két tizedesjegyre. Ha egyetlen jegy sem volt, ezt írd ki: Nem adtál meg jegyet.

Jegy (0 = vége): 4
Jegy (0 = vége): 5
Jegy (0 = vége): 3
Jegy (0 = vége): 0
3 jegy, átlag: 4.00
osszeg = 0
db = 0
jegy = int(input("Jegy (0 = vége): "))
while jegy != 0:
    osszeg += jegy
    db += 1
    jegy = int(input("Jegy (0 = vége): "))
if db == 0:
    print("Nem adtál meg jegyet.")
else:
    print(f"{db} jegy, átlag: {osszeg / db:.2f}")
Tipp

Végjelig olvasás: egy beolvasás a ciklus előtt, egy a mag végén. A 0 jegy esetét a ciklus után kell kezelni, mert 0-val nem lehet osztani.

Számháromszög

Kérj be egy pozitív egész számot (n), és rajzolj n soros számháromszöget: az i-edik sorban 1-től i-ig állnak a számok, szóközzel elválasztva.

n = 4
1
1 2
1 2 3
1 2 3 4
n = int(input("n = "))
for sor in range(1, n + 1):
    for szam in range(1, sor + 1):
        print(szam, end=" ")
    print()
Tipp

Két egymásba ágyazott ciklus: a külső a sorokat számolja (sor 1-től n-ig), a belső az adott sor számait írja ki (1-től sor-ig). A belső ciklus után print() kezd új sort. A sorvégi szóköz nem számít hibának.

Prímszám-e?

Kérj be egy egész számot (legalább 1), és döntsd el, hogy prímszám-e. Prímszám az a 2-nél nem kisebb szám, amelynek csak két osztója van: 1 és önmaga.

Szám: 97
97 prímszám.

Különben: 91 nem prímszám. (Az 1 sem prímszám.)

szam = int(input("Szám: "))
prim = szam >= 2
oszto = 2
while oszto * oszto <= szam and prim:
    if szam % oszto == 0:
        prim = False
    oszto += 1
if prim:
    print(f"{szam} prímszám.")
else:
    print(f"{szam} nem prímszám.")
Tipp

Próbáld végig az osztókat 2-től. Ha valamelyik osztja a számot, az nem prím (állíts egy jelzőt False-ra, vagy lépj ki break-kel). Elég a szám négyzetgyökéig keresni: ha oszto * oszto > szam, nagyobb osztópár már nem lehet. Az 1-et külön kell kezelni.

Ellenőrzött bevitel

Kérj be egy érdemjegyet (1–5). Ha a beírt szám nem 1 és 5 közé esik, írd ki: Hibás érték., és kérd újra – egészen addig, amíg jó értéket nem kapsz. A végén írd ki: Rögzítve: 4.

Jegy: 7
Hibás érték.
Jegy: 0
Hibás érték.
Jegy: 4
Rögzítve: 4
while True:
    jegy = int(input("Jegy: "))
    if 1 <= jegy <= 5:
        break
    print("Hibás érték.")
print("Rögzítve:", jegy)
Tipp

A while True + break minta (6. fejezet): a ciklusban beolvasol, és ha az érték jó, break-kel kilépsz; különben kiírod a hibaüzenetet, és a ciklus újrakezdődik. (Megoldható while jegy < 1 or jegy > 5: feltétellel is.)

Fizz-Buzz

A klasszikus programozói feladat: kérj be egy pozitív egész n-t, és írd ki a számokat 1-től n-ig, mindegyiket külön sorba – de a 3-mal osztható számok helyett Fizz, az 5-tel oszthatók helyett Buzz, a mindkettővel oszthatók helyett FizzBuzz álljon.

n = 15
1
2
Fizz
4
Buzz
…
14
FizzBuzz
n = int(input("n = "))
for i in range(1, n + 1):
    if i % 15 == 0:
        print("FizzBuzz")
    elif i % 3 == 0:
        print("Fizz")
    elif i % 5 == 0:
        print("Buzz")
    else:
        print(i)
Tipp

Ugyanaz a csapda, mint a 2. modul oszthatósági feladatában: a „mindkettővel osztható” esetet kell először vizsgálni.

10. fejezet

Összefoglalás

EszközLényeg
for x in sorozat:a sorozat minden elemére lefut; x körönként a következő elem
range(start, stop, step)számsorozat; a stop már nem része; range(n) = 0…n−1
while feltétel:amíg a feltétel igaz; minden kör előtt újra kiértékeli
összegzés, számlálásgyűjtőváltozó a ciklus előtt 0, a magban +=
szélsőértékkezdőérték az első elem; nagyobbat találva csere
break, continuekilépés a ciklusból / ugrás a következő körre
beágyazott ciklusa belső ciklus a külső minden körében végigfut

Gyakorold tovább: írj programot, amely bekéri egy osztály dolgozatainak pontszámait végjelig (−1), és kiírja a legjobb és a leggyengébb pontszámot, az átlagot, valamint hogy hányan értek el legalább 50 pontot. Előbb gondold végig: hány gyűjtő- és számlálóváltozó kell, és mi legyen a kezdőértékük?