Ügyességi játékok programozása
Pythonnal és Pygame-melChapter 9: Függvények
9.1 Bevezető a függvényekhez
Függvényeket két okból használunk. Először is, ez könnyebbé teszi a kódunkat mind olvasni, mind megérteni. Másodsorban, lehetővé teszik, hogy a kódunkat többször is felhasználjuk, ne csak egyszer.
Képzelj el egy kódcsoportot, amik játékautókat vezérelnek. Mondjuk, a programozó szeretné, hogy a játékautó előre menjen. Ehhez, a következő parancsokat hajtatja végre:
turnOnMotor() pauseOneSecond() turnOffMotor()
Ha definiáljuk a függvényeket, akkor a program olvashatósága megnő és újra lehet használni, bármennyiszer.
def moveForward(): turnOnMotor() pauseOneSecond() turnOffMotor()
Önmagában ez a kód nem fogja még előre mozgatni az autót. Még meg kell adnunk a számítógépnek, hogy hogyan tegye az előre mozgatást. Neked kell meghívnod a függvényt az aktuálisan futott kód szerint, hogy az autó elinduljon:
moveForward()
Ha van egy teljes könyvtárunk ilyen funkciókkal, akkor az autó mozgatása, mindössze így néz ki:
moveForward() turnLeft() moveForward() moveForward() turnRight() moveForward()
Emlékezz arra, hogy a moveForward parancsnak három sora van. Minden ilyen parancs több sor kódot foglal magába. Ezeket a függvényeket használva, ismételhetjük a parancsokat akár anélkül, hogy az összes parancsot ismételtetnénk, amit magába foglal, így hozva létre kisebb programokat.
A függvénynevek nagyon fontosak. Ha a függvény nevek leíró jellegűek, akkor még egy nem-programozó is képes lesz megérteni a kód egy részét és valamiféle elképzelést kialakítani arról, hogy mit is csinál. A függvénynevekre ugyanazok a szabályok vonatkoznak, mint a változókra, és kisbetűvel kell, hogy kezdődjenek.
9.2 A függvények paraméterei
A függvényeknek van paraméterük. Ezek arra jók, hogy megnöveljék a flexibilitását a függvénynek, megmondva neki azt, hogy mit cselekedjen. Például, egy függvény, amit moveForward() -nak hívnak, előre mozgat egy robotot egy másodpercre. De a függvényt meg lehet változtatni azzal, hogy adunk neki egy paramétert, ami azt határozza meg, hogy hány másodpercig. A példánál maradva, moveForward(3) előre fogja mozgatni 3 másodpercig, míg a moveForward(6) már hat másodpercig.
A robotot irányító függvényünk valahogy így néz ki:
def moveForward(time): turnOnMotor() for i in range(time): pauseOneSecond() turnOffMotor()
Itt van egy másik függvény, ami robot autó nélkül is elfut. Ez a függvény kiszámolja és kiírja egy gömb térfogatát:
def volumeSphere(radius): pi = 3.141592653589 volume = (4 / 3) * pi * radius ** 3 print("The volume is", volume)
A függvény neve volumeSphere. Az adat, amit a függvénynek megadunk, egy új változóban fog tárolódni, amit radius-nak hívunk. Az eredmény, a térfogat ki lesz íratva a képernyőre. A radius. változónak nincs értéke még itt. Az újdonsült programozók sokszor vétenek, amikor értéket akarnak adni a függvény paramétereinek a létrehozásukkor, ez nem legális. A paramétereknek akkor adunk értéket, amikor a függvényt meghívjuk:
Ha meg akarod hívni a függvényt, tedd így:
volumeSphere(22)
A radius változó a függvényben 22-re van kezdetben állítva. A függvény kódja lefut egyszer és végrehajtja egyszer, amit kell.
Mi van akkor, ha több, mint egy értéket szeretnénk vizsgálni? Több paraméter is adható egy függvényhez, ha mindegyiket elválasztjuk vesszővel.
def volumeCylinder(radius, height): pi = 3.141592653589 volume = pi * radius ** 2 * height print("The volume is", volume)
Ezt a függvény így hívhatjuk meg:
volumeCylinder(12, 3)
A paraméterek sorrendben hajtódnak végre, így a radius értéke 12 lesz, majd a height változó értéke kapja a 3-t.
Sajnos, ezek a példa függvények végesek. Miért? Ha valaki használni akarja a volumeCylinder függvényt, hogy megkapja a térfogatát hat hengernek, ez nem fog menni. Ez csak egy henger térfogatát írj ki. Nem lehetséges felhasználni a függvény eredményeit, és hat térfogatra számoltatni.
Ezt a problémát kiküszöbölhetjük a return paranccsal. Például:
def volumeCylinder(radius, height): pi = 3.141592653589 volume = pi * radius ** 2 * height return volume
A return nem egy függvény, és nem kell zárójelet sem használnunk. Ne csinálj ilyet, hogy return(volume).
Mivel a return benne van a függvényben, ezért már később is felhasználhatjuk, akár hatszor is:
sixPackVolume = volumeCylinder(2.5, 5) * 6
Az érték, amit a volumeCylinder ad vissza, az egyetlenben végrehajtódik, és meghatszorozzuk.
Nagy a különbség a függvény közt, ami csak print-el, vagyis kiírat egy értéket, és aközött, amelyik visszaad(returns) egy értéket. Nézd meg a lenti kódot és próbáld ki:
#A függvény, ami kiírja az eredményt def sumPrint(a, b): result = a + b print(result) # A függvény, ami visszaadja az eredményt def sumReturn(a, b): result = a+b return result # Ez kiírja 4+4 eredményét sumPrint(4, 4) # Ez nem sumReturn(4, 4) # Ez nem fogja x1-nek az összeg értékét adni # Valóságban a 'None' értéket kapjuk majd x1 = sumPrint(4, 4) # Ez igen x2 = sumReturn(4, 4)
9.3 Függvények dokumentálása
Pythonban a függvényeknek tipikusan az első parancssora egy komment. Ez a komment három dupla-idézőjel által van közrefogva, és úgy hívják, hogy docstring. Egy függvény így néz ki:
def volumeCylinder(radius, height): """Visszaad egy hengertérfogatot, a megadott sugár és magasság alapján.""" pi = 3.141592653589 volume = pi * radius ** 2 * height return volume
A jó dolog a docstring használatában az, hogy a komment kiemelhető, és a webre is kirakható, olyan eszközzel, amit úgy hívnak, hogy Sphinx. A legtöbb nyelv rendelkezik hasonló eszközzel, ami segít dokumentálni. Ez elég sok időt megtakarít, amikor nagyobb programokkal dolgozol.
9.4 Változó hatóköre
A függvény használata bevezet egy olyan fogalmat, mint a hatókör. A hatókör az, ahol a kód változója él, ahol hozzáférhető. Példának, lásd alább:
# definiál egy egyszerű függvényt, ami # x-t 22-vel tesz egyenlővé def f(): x = 22 #A függvény meghívása f() # Ez hibás, az x csak az f()-n belül létezik print (x)
Az utolsó sor egy hibát fog generálni, mivel x csak az f() függvényen belül létezik. A változó, amit alkottunk, amikor az f() függvényt meghívjuk, és a memória, ahol tároltuk, törli, amint az f() végrehajtódott.
Sokkal zavaróbb szabály az, amely megengedi, hogy az f() függvény, a rajta kívül létrehozott változóhoz is hozzáférjen. A következő kódnál, x-t még azelőtt hozzuk létre, hogy f() függvényt létrehoznánk, és így fogjuk beolvasni az f() függvénybe.
# Készítsünk egy x változót, ami x-t 44-gyel teszi egyenlővé x = 44 # Definiáljunk egy egyszerá függvényt, ami kiírja x-t def f(): print(x) # Függvény meghívása f()
A változók amiket egy függvény előtt hoztunk létre, olvashatóak a függvényben, de csak akkor, ha nem változtatjuk meg értékét a függvényen belül. Ez a kód, nagyon hasonló a fentihez, vagyis hibás. A számítógép nem fogja érteni, hogy az x mire vonatkozik.
# Készítünk egy x válozót, és feltöltkük a 44 értékkel x=44 # Definiálunk egy egyszerű függvényt, ami kiírja x-t def f(): x += 1 print(x) # Függvény lehívása f()
Más nyelveknek sokkal összetettebb szabályai vannak a változók létrehozásával kapcsolatban és hatókörével, mint a pythonnak van. Mivel a Python előrelátó, ezért jó bevezető nyelv.
9.5 Másolás általi átadás
A függvények átadják az értéküket azzal, hogy lemásolják azt. Például:
# Definiáluink egy egyszerű függvényt, ami kiírja x-t def f(x): x += 1 print(x) # y megadása y = 10 # Függvény meghívása f(y) # Az y kiírása, hogy lássuk változott-e print(y)
Az y értéke nem változik, még akkor sem, ha az f() függvény megnöveli az átadott értéket. Mindkét változó egy paraméter, és a függvényben egy új paraméter. A változó értéke lemásolódott onnan, ahonnan meghívták azt.
Ez érthető, ha tovább vizsgálunk egy példát. Ahol kicsit zavaró lehet az, hogy a kód is és a függvény is ugyanolyan nevű változót használ. A lenti kód csak jelképes a listát tekintve, de szerencsésebb y helyett az x-t használni.
# Definiál egy egyszerű függvényt, ami kiírja x-t def f(x): x += 1 print(x) # x megadása x = 10 # Függvény megadása f(x) # Írassuk ki x-t, hogy lássuk megváltozott-e print(x)
A kimenet ugyanaz, mintha a program y -t használna változónévként. Noha, mindkét program x-t használja változónévként, nekünk két különböző változónk van. Van egy x változónk a függvényen belül, és egy a függvényen kívül.
9.6 Függvények, amik függvényeket hívnak
Az is lehetséges, hogy egy függvény meghívjon egy másik függvényt. Például, mondjuk egy ilyen függvényed van:
def armOut(whichArm, palmUpOrDown): #a kód itt fog futni def handGrab(hand, arm): # a kód itt fog futni
Azután egy másik függvényt hozz létre, ami meghívja az előzőt:
def macarena(): armOut("right", "down") armOut("left", "down") armOut("right", "up") armOut("left", "up") handGrab("right", "left arm") handGrab("left", "right arm") # etc
9.7 Főbb függvények és globálok
Amint a program nagyobb lesz, fontossá válik, hogy a kód szervezettt maradjon, és még így is működjön. A Python lehetővé teszi, hogy olyan kódot írjunk, ami a nullás bekezdés szintjén van. Ez jellemzi a legtöbb programot, amit eddig írtunk. A kódjaink balra igazodtak, és nem kerültek függvényekbe.
Ez az elgondolás olyan, mint a ruhák halomba rakása a padló közepén, vagy mikor minden szerszámot egy csomóban tartasz egy munkaasztalon. Ez csak akkor működik, amikor nincs sok tennivalód. De, még akkor is rendetlennek hat.
Minden kódod és minden változód betehető egy függvénybe. Ez fogja rendszerezetten tartani a kódodat. Segíteni fog akkor is, amikor egy bugot keresel a programban. Változóid, amiket a nullás bekezdési szinten hozol létre, a globális változók. Globális változók elég rossz dolgok. Miért? Mivel a kód bármely részén meg lehet változtatni az értékét. Ha van egy 50.000 soros programod, minden sorod megváltoztathatja a változód. Ha viszont egy függvényben tartod, akkor csak ez a függvénykód képes megváltoztatni a változódat. Így, ha van egy nem várt értéked a változóban, akkor is csak 50 sor kódot kell átnézned a függvényben. Máskülönben, át kellene néznes az egész program összes sorát.
Jobb módszer programírásra Pythonban a következő minta:
def main(): print("Hello world.") main()
Ez esetben az összes kód, amit én a nullás bekezdési szinten futtatok be lett téve egy main függvénybe. Az utolsó sora a fájlnak hívja meg a main-t.
Na várjunk csak! Van itt még egy probléma, amit korrigálnunk kell. A 15-dik fejezetben, beszélni fogunk arról, hogyan tördeljük a programunkat több fájlba. Használhatjuk az import parancsot, hogy betöltsük a függvényt más általunk kreált modulból. Ha az import parancsot meghívjuk, akkor ez automatikusan lefuttatja a main függvényt. Nem akarjuk ezt. Olyan programot akarunk, amit tudunk irányítani.
A probléma kijavítására meg kellene tesztelnünk a programunkat egy globális változóra, amit a Python automatikusan definiál. (Tudom, most mondtam, hogy a globál rossz, jó?) Ezt a változót úgy hívják: __name__, két aláhúzással előtte és utána is. Ezzel ellenőrízhetjük, hogy egy kód importálva lett vagy futtatva. Ha a kódunk fut, akkor a Python automatikusan átállítja ezt a változó értéket __main__-re. Egy egyszerű if paranccsal csak meghívjuk a main függvényt, ha kód fut. Máskülönben a kód csak definiálja a main függvényt. A kódot, ami importálva lett, ez meg tudja hívni, amikor szükségünk van rá.
Így néz ki az, ahogyan minden Python kódnak futnia kéne:
def main(): print("Hello world.") if __name__ == "__main__": main()
Az egyik ok, amiért szeretem a Pythont az az, hogy első nyelvként neked nem kell ilyesmivel foglalkoznod mindaddig, amíg nem szükséges. Más nyelvek, mint a Java, már használják már akkor is, amikor kicsiny a programod.
Azért, hogy könnyebbé tegyük a dolgokat, nem fogunk bemutatni példát ezekre a mintákra. De ez után a könyv után a programjaid elég összetettek lesznek ahhoz, hogy az életed könnyebbé váljon akkor, ha "nem dobod az összes ruhát egy kupacba a földön".
Ha te egy szuper-lelkes egyén vagy a programozást illetően, akkor próbáld a programjaidat már ezen elvek szerint írni mostantól. Noha kezdetben sokkal nehezebb lesz, később meghálálja magát. Továbbá jó, ha megtanulod, hogyan menedzseld az adataidat és hatókörüket.
Itt egy példa arra, hogy milyen PyGame sablont használj ezen minták szerint:
pygame_base_template_proper.py
Nem muszáj ezt a sablont használni, ha én tartom neked ezt a kurzust. Nekem nem jelent gondot, hogy a ruháidat a földre hajigálod az első szemeszterben. Már annak is örülök, hogy ruhában vagy. (A tisztaságmániákusok részére jegyzem meg, hogy ezt a programot majd tovább tisztítjuk az osztályok fejezetben.)
9.8 Review
9.8.1 Multiple Choice Quiz
Click here for a multiple-choice quiz.
9.8.2 Short Answer Worksheet
Click here for the chapter worksheet.
9.8.3 Lab
Click here for the chapter lab.
You are not logged in. Log in here and track your progress.
English version by Paul Vincent Craven
Spanish version by Antonio Rodríguez Verdugo
Russian version by Vladimir Slav
Turkish version by Güray Yildirim
Portuguese version by Armando Marques Sobrinho and Tati Carvalho
Dutch version by Frank Waegeman
Hungarian version by Nagy Attila
Finnish version by Jouko Järvenpää
French version by Franco Rossi
Korean version by Kim Zeung-Il
Chinese version by Kai Lin