# 2. hét: nyomkövetés, vezérlés

Czirkos Zoltán, Frey Balázs, Bulla Ádám · 2024.09.22.

Nyomkövetés. Vezérlési szerkezetek: elágazások, ciklusok. Összetett vezérlési szerkezetek, összefüggő ciklusok és független változók.

Felkészülés a laborra:

- A vezérlési szerkezetekről tanultak megértése.

## 1. Kettő meg három

Adott az alábbi program.

    egyik = input("Írj be valamit: ")
    masik = input("Írj be még valamit: ")

    print(egyik + masik)

Másold be a fejlesztőkörnyezetbe, és futtasd le! Mi történik akkor, ha azt írod be neki, hogy alma, és hogy fa? És mi történik akkor, ha 2-t és 3-at írsz be neki? (Miért nem 5?) Magyarázd meg (magyar nyelvű szövegben), hogy mi történik!

Ha ez megvolt, írd át a programot ilyenre:

    egyik = int(input("Írj be valamit: "))
    masik = int(input("Írj be még valamit: "))

    print(egyik + masik)

Figyeld meg, mi a különbség a kódban! Az első két sor változott, a harmadik nem. Próbáld most ki 2-vel és 3-mal! Mit tapasztalsz? Mi történik akkor, ha most próbálod beírni az előbbi szavakat? Magyarázd meg az eredményt!

## 2. Sztringek különleges karakterekkel

Az előadás szorzótáblás programjánál szóba jött, hogy létezik egy tabulátor nevű karakter, és hogy azt `"\t"`-vel kell a forráskódban megadni. Ennek kapcsán pedig arról is, hogy a különleges karaktereket `\` visszaperrel kell jelölni, ha szöveg belsejében szerepelnek a forráskódban.

Írd ki egyetlen egy `print()` utasítással az alábbi szövegeket! Ügyelj arra, hogy ahol két sorban vannak a minta szerint a kimenetek, ott a programodnak is úgy kell megjelenítenie őket.

    A print('hello') kiírja, hogy "hello",
    és kezd egy új sort.

    A "\t" karaktert tabulátornak nevezzük.
    A következő 8-cal osztható sorszámú oszlopra lehet ugrani vele.

## 3. A tartály

Írj programot, amely segít kiszámolni a felhasználónak, hogy hány doboz festéket kell vennie a lábakon álló tartály festéséhez! Ehhez használnod kell majd az első előadásokon tanultakat. Indulj ki az előadáson bemutatott kör kerülete és területe programból!

A tartálynak ismerjük a magasságát és az átmérőjét. Mindenhol le kell festeni, az oldalát, a tetejét és az alját is. Tudjuk azt is, hogy egy doboz festék 2 m2 felület lefestéséhez elegendő.

    Tartály festése

    Milyen magas? 2
    Mennyi az átmérője? 1.2

    4.900885 doboz festék kell.

Figyelj arra, hogy a program kódjában tizedesvessző helyett tizedespontot kell használni (pl. `3.14`, persze jó a `math.pi` is), ugyanígy a bemenet megadásánál is. Ha nem jön ki a fenti eredmény, gondold át, helyes-e a képlet, amit használsz.

Megoldás

    print("Tartály festése")

    magas = float(input("Milyen magas? "))
    atmero = float(input("Mennyi az átmérője? "))

    sugar = atmero/2
    doboz = (2 * sugar**2 * math.pi + magas * 2 * sugar * math.pi) / 2

    print(doboz, "doboz festék kell.")

## 4. Szakasz hossza

Írj programot, amely a felhasználótól bekéri két síkbeli pont `x` és `y` koordinátáit, és kiírja a közéjük húzott egyenes szakasz hosszát (Pitagorasz tételével)!

szakasz | hossz
--- | ---
`(0;0)–(1;1)` | `1.414214`
`(1;5)–(4;1)` | `5`
`(-3;2)–(5;7)` | `9.433981`

Megoldás

    import math

    x1 = float(input("x1 = "))
    y1 = float(input("y1 = "))
    x2 = float(input("x2 = "))
    y2 = float(input("y2 = "))
    hossz = math.sqrt((x1-x2)**2 + (y1-y2)**2)
    print(hossz)

> ### Hasonló feladatok

> Ha ez a feladat nehezen ment, megoldhatsz pár hasonló feladatot a példatárból, mielőtt a következő feladatra rátérsz.

> Vigyázz, a laborfeladatokat erősen ajánlott az utolsó feladatig megoldani, hogy a jövő hétre felkészült legyél. Ha nem sikerül, fejezd be őket otthon!

## 5. Másodfokú egyenlet

Írj programot, amely az `ax2+bx+c=0` másodfokú egyenlet együtthatóit kérdezi a felhasználótól! Ezek valós számok. Ezek után írja ki az egyenlet `x1` és `x2` megoldását! A megoldóképlet:

Próbáld ki a bal oldalt látható egyenletekre! Ezekkel ellenőrizni tudod a megoldásod. Próbáld ki aztán a jobb oldali egyenletekre is. Mit tapasztalsz? Miért?

egyenlet | megoldás
--- | ---
`2x2-x-6 = 0` | x1=2, x2=-1.5
`x2-12x+35 = 0` | x1=5, x2=7

egyenlet | megoldás
--- | ---
2`x2-4x+2 = 0` | ?
`x2+2x+10 = 0` | ?

Írd át úgy a programot, hogy figyelembe vegye azokat az eseteket, amikor nincs, vagy csak egy valós gyök van, és eszerint végezd a kiírást! Ehhez a diszkriminánst kell megvizsgálni, ami a gyökjel alatti rész. Például, ha ott negatív szám van, nincsen valós megoldás, mert nem lehet negatív számból gyököt vonni.

Megoldás

Arra kell figyelni, hogy ne vonj negatív számból négyzetgyököt. A gyökvonás előtt a diszkrimináns vizsgálatával meg kell nézni, hány valós megoldás lesz.

Itt egy nagyobb kifejezést kapsz, amiben többféle operátor (összeadás, kivonás, szorzás stb.) szerepel. Ezeknek a műveleteknek Pythonban ugyanúgy van precedenciája, mint a matematikában. A szorzás pl. magasabb rendű művelet, mint az összeadás. A nyelv precedencia szabályai összetettebbek; erről majd előadáson lesz szó.

A `diszkr == 0` vizsgálat elméletben helyes, gyakorlatban azonban nem előnyös ilyet írni. A kerekítési hibák miatt előfordulhat az, hogy 0-hoz olyan közeli szám adódik, amit már 0-ra kerekít a számítógép. Erről is később lesz szó előadáson.

    import math
    a = float(input("a: "))
    b = float(input("b: "))
    c = float(input("c: "))

    diszkr = b**2 - 4* a * c

    if diszkr > 0:
        x1 = (-b - math.sqrt(diszkr)) / (2*a)
        x2 = (-b + math.sqrt(diszkr)) / (2*a)
        print("x1: ",x1)
        print("x2: ",x2)
    elif diszkr == 0:
        print(-b / (2*a))
    else:
        print("Az egyenletnek nincs megoldása")

> ### Hasonló feladatok

> Ha ez a feladat nehezen ment, megoldhatsz pár hasonló feladatot a példatárból, mielőtt a következő feladatra rátérsz.

> Vigyázz, a laborfeladatokat erősen ajánlott az utolsó feladatig megoldani, hogy a jövő hétre felkészült legyél. Ha nem sikerül, fejezd be őket otthon!

## 6. Számtani sorozat

Olyan programot kell írnod, amelyik számtani sorozatot jelenít meg.

- A számtani sorozat differenciáját a felhasználó adhassa meg! A sorozat két határát (alsó és felső) is! Ezek egész számok kell legyenek.
- Hogy „reagálja le” a program azt, ha rossz sorrendben adod meg a határokat? Javítsd meg, hogy ilyenkor is működjön! Ezt legjobb úgy csinálni, ha a két határt nem `mn` és `mx`, hanem valamilyen semleges nevű változóba teszed először (pl. `hatar1`, `hatar2`), mert amíg nem vizsgáltad meg őket, addig nem jó ötlet minimumnak és maximumnak nevezni. Tehát olvasd be őket, vizsgáld meg a sorrendet, és utána kerüljenek `mn`-be és `mx`-ba a számok! Hogy miért nem `min` és `max` a változók nevei, arról majd később tanulunk. (itt nem okozna egyébként gondot...)

Megoldás

    diff = int(input("Mennyi a sorozat differenciája?"))
    hatar1 = int(input("Mennyi a sorozat egyik határa?"))
    hatar2 = int(input("Mennyi a sorozat másik határa?"))

    if hatar1 <= hatar2:
        mn = hatar1
        mx = hatar2
    else:
        mn = hatar2
        mx = hatar1

    szam = mn
    while szam <= mx:
        print(szam)
        szam = szam+diff

> ### Hasonló feladatok

> Ebben a témakörben is találsz még feladatokat a példatárból. Ha úgy érzed, oldj meg onnan néhányat a továbblépés előtt. De vigyázz, az itteni feladatokat ajánlott végig megoldani a jövő hétre, hogy felkészült legyél.

## 7. A nyomkövető használata

A programozási hibákat gyakran nehéz megtalálni, de sokat segít a nyomkövetés. Ennek segítségével a programot megállíthatjuk futás közben, végrehajthatjuk soronként, közben megfigyelhetjük a változók értékeit. Ez azért jó, mert:

- Rögtön észre tudjuk venni, ha épp „elromlott” egy változó: rossz volt a gondolatmenetünk, algoritmusunk, és nem azt tettük bele, amit szerettünk volna.
- Azt is látjuk, ha kódolási hibánk van; ha nem azt a műveletet végzi el a programsor, mint amit szántunk neki. Például mert lefelejtettünk egy zárójelet, tehát kódolási hibánk van.

A nyomkövetés kipróbálásához, megtanulásához tedd a következőket. Először is másold be ezt a programot egy új fájlba:

    n = 1
    while n <= 10:
        print(n, end=" ")     # nem kezd új sort
        n += 1
    print(".")

Mentés után a Shell ablak menüjében kattints a `Debug / Debugger` menüpontra. Erre megjelenik egy Debug Control nevű ablak. Ebben pipáld be a `[ ] Source` jelölőnégyzetet! Utána a program ablakában a szokásos `Run / Run module` menüpontra kattints.

A program ilyenkor elindult, de még egyelőre meg van akasztva a legelső sor előtt. A Debug Control ablak `Over` gombjának nyomkodásával lehet végrehajtani a következő sort. A befejezett ciklustörzsek után a kimeneten elkezdenek megjelenni a számok. A forráskódban látszik, melyik az aktuális sor, a szürke sáv mutatja, hol tart épp a végrehajtás. A Debug Control ablakban pedig (sok jelenleg nem lényeges információ mellett) a változó értéke figyelhető meg:

Kattints még sokszor az `Over` gombra, közben figyeld a kimenetet és a Shell ablakot!

Miután kitapasztaltad, válaszolj a nyomkövető használatával az alábbi kérdésekre!

- Mennyi az alábbi programban az `n` változó értéke, amikor a legalsó sorhoz ér a végrehajtás, a `print(".")`-hoz?

      n = 1
      while n <= 10:
          print(n, end=" ")     # nem kezd új sort
          n += 1
      print(".")

- Alább egy program, amely 8 faktoriálisát számítja ki, és 40320-at ír a kimenetre.

      szorzat = 1
      n = 8
      while n > 1:
          szorzat *= n
          n -= 1
      print(szorzat)

  Értsd meg a nyomkövető segítségével a program működését! A nyomkövető használatával – újabb `print()` beírása nélkül – mondd meg, hogy mennyi a `szorzat` változó értéke, amikor az `n` változó éppen `3`-ra változott!
  Megoldás

  A válasz: szorzat = 6720. Mivel a ciklus visszafelé megy, ez a változó nem a faktoriálisok értékeit veszi fel menet közben.

- A következő program Euklidész algoritmusa segítségével határozza meg két szám legnagyobb közös osztóját, jelen esetben az 11220-ét és a 2002-ét, ami 22:

    a = 11220
    b = 2002
    while b > 0:
        temp = b
        b = a % b
        a = temp

    print("lnko =", a)

Megint új `print()` beírása nélkül: mennyi a `b` változó értéke, amikor az `a` épp 374-re változott?
Megoldás

Itt: b = 44.

## 8. Adott hosszúságú szakasz

Írj egy programot, amely kér a felhasználótól egy számot, és kirajzol egy + és − jelekből álló szakaszt. Pl. ha a szám 4, akkor a képernyőn a lenti ábra jelenjen meg, vagyis a belsejében 4 db − legyen:

    Mekkora legyen a szakasz?
    4
    +----+

Írd meg a programot előbb úgy, hogy ciklust használsz a – jelek kiírásához!

Aztán próbáld ki, mi történik a `print("-" * 4)` sor hatására. Hogy lehetne ezt felhasználni? Írd meg így is a programot!

Megoldás

Ciklussal:

    print("Mekkora legyen a szakasz?")
    hossz = int(input())
    print("+", end="")
    i = 0
    while i < hossz:
        print("-", end="")
        i = i+1
    print("+")

Sztring többszörözésével:

    print("Mekkora legyen a szakasz?")
    hossz = int(input())
    print("+", end="")
    print("-" * hossz, end="")
    print("+", end="")

Még rövidebben:

    print("Mekkora legyen a szakasz?")
    hossz = int(input())
    print("+", "-" * hossz, "+", sep="")

## 9. Adott méretű négyzet

Az előző feladat rajzolást végző programrészéből készíts egy olyan másolatot, amely + és - karakterek helyett | és . karaktereket használ, mint jobb oldalon! Végül pedig, ezt ciklusba téve, írj egy olyan változatot, amelyben egy adott méretű négyzet jelenik meg.

    |....|

    +----+
    |....|
    |....|
    |....|
    |....|
    +----+

A forráskód szerkesztését kényelmesebb billentyűzeten végezni, mint egérrel. Szinte minden funkció elérhető gyorsbillentyűkön keresztül. Néhány fontosabb:

- `Home`, `End`: sor elejére, sor végére ugrás,
- `Shift` + `↑`, `↓`, `←`, `→`: forráskódrészlet kijelölése,
- `Ctrl` + `]`, `Ctrl` + `[`: a kijelölt részlet indentálása (különösen hasznos ennél a feladatnál: amikor egy meglévő kódrészletet szeretnél ciklusba betenni, vagy ciklusból kivenni!),
- `Ctrl` + `C`, `Ctrl` + `V`: a szokásos másolás és beillesztés,
- `Ctrl` + `Z`: „undo”, szerkesztés visszavonása.

Megoldás

Ciklussal:

    print("Mekkora legyen a négyzet?")
    hossz = int(input())

    # Felső sor
    print("+", end="")
    i = 0
    while i < hossz:
        print("-", end="")
        i = i+1
    print("+")

    # Közepe
    j = 0
    while j < hossz:
        print("|", end="")
        i = 0
        while i < hossz:
            print(".", end="")
            i = i+1
        print("|")
        j = j+1

    # Alsó sor, mint a teteje
    print("+", end="")
    i = 0
    while i < hossz:
        print("-", end="")
        i = i+1
    print("+")

Sztring többszörözésével lehetne rövidebben is.

## 10. Háromszög golyókból

Írjunk programot, amely kér a felhasználótól egy számot (n), és utána egy akkora háromszöget rajzol a képernyőre „o” betűkből, hogy annak éppen a megadott számú sora van! Például n=3 esetén az itt látható ábra keletkezzen.

      o
     ooo
    ooooo

Megoldás

A feladat megoldásának kulcsa egy rajz készítése… És annak meghatározása, hogy melyik sorban hány golyó és hány szóköz van. Aki ide eljut, annak nyert ügye van.

- Minden sor egyforma: sok szóköz, aztán sok golyó. A rajzon pötty helyettesíti a kiírandó szóközöket.
- Egy sor kiírása: ciklusban szóköz, ciklusban golyó. Aztán új sor kezdése.
- Ha ez megvan, ezt kell ciklusba tenni.

    ···o
    ··ooo
    ·ooooo
    ooooooo

Ciklusokkal:

    magas = int(input("Milyen magas legyen a kupac?"))

    sor = 0
    while sor < magas:
        # A sorok elejére kellenek szóközök
        oszlop = 0
        while oszlop < magas-sor-1:
            print(" ", end="")
            oszlop = oszlop+1

        # Utána valamennyi darab o
        oszlop = 0
        while oszlop < sor*2+1:
            print("o", end="")
            oszlop = oszlop+1
        print()
        sor = sor+1

Sztring többszörözésével:

    print("Milyen magas legyen a kupac?")
    magas = int(input())

    sor = 0
    while sor < magas:
        print(" " * (magas-sor-1) + "o" * (sor*2+1))
        sor = sor+1

> ### Hasonló feladatok

> Itt mindig az a lényeg, hogy a rajz alapján rájöjj, milyen vezérlési szerkezettel állítható az elő. Ebben a témakörben is találsz még feladatokat a példatárból.